# Shared fields — V1 checkpoint

Current implementation: [fields.html](../fields.html). Shared values are in [Sass tokens](../scss/_tokens.scss); optional component rules are in [field styles](../scss/_field-styles.scss). Generated theme.css/fields.css are outputs, not editable sources. R027 supersedes the R013 resting-border and focus-color refinements after user feedback: borders now follow the wizard. Accepted readable text/placeholder/error roles remain explicit differences.

## Geometry and typography

- Noto Sans, 16px/24px, weight 400 for values/placeholders. Labels use 14px/20px, weight 500, with 8px bottom spacing.
- Primary radius 14px; modal modifier radius 10px. Padding 12px block / 16px inline, single 1px contour and 2px focus ring.
- Input/single-select height measures 50px; three-row textarea measures 98px. Heights remain content-driven, not fixed clipping boxes.
- Primary textarea resizes vertically; modal textarea does not. Native select line-height can report normal; verify rendered dimensions instead.

## Current contrast roles

| Role | Light | Dark |
|---|---|---|
| Field fill | white | black/20 over the actual backdrop |
| Resting border | source gray-300 OKLCH | white/20 |
| Placeholder | #6b7280 | white/80 |
| Primary focus border / ring | #4a9fd8 / same color at 50% | source blue-400 at 50% / same |
| Modal focus ring | #4a9fd8 at 20% | source blue-400 at 20% |
| Error text | #b91c1c | #fca5a5 |
| Error border | source red-500 OKLCH | #fca5a5 |

Placeholder pseudo-element opacity is 1; alpha lives in the token. Normal values retain text-primary. Primary and modal focus now use their source-specific 50% and 20% rings. Primary selects preserve the resting contour on focus, matching the wizard; modal selects use the focus border. Error borders retain the accepted red tokens and override focus contours. Separate field-control focus roles avoid changing accepted button/chip focus indicators.

## R027 border correction

The earlier integrated reference retained stronger R013 contours (white/65 dark and #858b94 light). Those were visibly different from the requested wizard baseline. Default, hover, disabled and readonly contours now use source white/20 or gray-300; primary/modal focus follows the actual wizard families. Geometry, field fill, text, placeholders and validation behavior are unchanged. This change supersedes historical contour/focus contrast thresholds: subtle source strokes and halos are not claimed to meet 3:1 independently. Recheck identification and keyboard focus on consuming backdrops.

Source files: WSDMM `src/app/components/wizard-steps/NetworkStep.tsx:114` and `src/app/components/AddDeviceModal.tsx:301`. [Current rendered border evidence](reference/field-border-verification.json) · [dark integrated view](reference/field-border-corrected-dark.png) · [light view](reference/field-border-corrected-light.png). Earlier final-state images remain historical, not current border authority.

## CSS and Sass consumption

```html
<link rel="stylesheet" href="theme.css">
<link rel="stylesheet" href="fields.css">
<div class="kb-field">
  <label class="kb-field-label" for="host">Hostname</label>
  <input class="kb-field-control" id="host" name="hostname"
         aria-describedby="host-help" placeholder="TestDevice01">
  <p class="kb-field-help" id="host-help">A name to identify the device.</p>
</div>
```

```scss
@use 'path/to/kb/scss' as kb;
@include kb.theme();
@include kb.fields();
```

Loading theme.css alone emits variables and scopes, with no reset or control styles. The consuming page owns Noto Sans loading. Use the theme's gradient/card roles documented in [theme usage](theme-usage.md); dark contrast depends on the actual composited backdrop.

Add `kb-field-control--modal` to the same control for the 10px variant. Primary field backdrop blur is 24px dark/0px light; modal controls override it to zero.

## Selects and Lucide caret

Wrap the native select in `.kb-select`, then add one `.kb-select-caret` SVG after it. It uses viewBox `0 0 24 24`, no fill, stroke currentColor, stroke-width 2, rounded caps/joins and path `m6 9 6 6 6-6` (Lucide ChevronDown). Set aria-hidden=true and focusable=false. The 16px icon sits 16px from inline end; select end padding is 44px. The icon cannot intercept pointer input. Disabled caret color follows disabled text. [License](../assets/licenses/lucide-LICENSE).

The empty option is a value, not a placeholder, so it uses value styling. Options receive explicit light/dark backgrounds where the browser permits styling; native menus remain platform-dependent. A plain select without the wrapper retains its native arrow. Forced colors hides the SVG and restores native appearance/padding. The custom caret is an intentional refinement over the wizard's native arrow, not source parity.

## State and semantic contract

| State | Required behavior |
|---|---|
| Default/hover | One contour; stable geometry and resting border on hover |
| Focus | Single 2px ring; select retains resting border unless modal; invalid retains error border |
| Invalid | Consumer sets aria-invalid=true and links explicit error text via aria-describedby; do not rely on red alone |
| Disabled | Native disabled, excluded from FormData and focus order; no focus shadow |
| Readonly | Native readonly for input/textarea; remains selectable, focusable and included in FormData |
| Select readonly | No native state; provide a text representation rather than silently dropping submission with disabled |

Labels must be associated with controls. Helper and error styles use the responsive description role (12/16 mobile, 14/20 above 640px), an explicit source difference. Do not mark pristine required fields invalid automatically. Consumer code owns validity and ARIA; CSS cannot supply them. The review demonstrates local required/email validation and reset with no network writes; it does not define a backend or device-domain validation rules.

Reduced motion removes transitions. Forced colors uses a system outline and differentiates invalid borders. Disabled fields have distinct styling; inactive contrast exemptions do not apply to readonly values.

## Verification and limitations

R013 verifies primary/modal default, focus, invalid and invalid-focus on actual light/dark cards at 1200/390px. Native keyboard/validation/recovery/reset, readonly FormData, disabled behavior, caret sizing and forced-colors fallback, reduced motion and 320px reflow are checked. Four Sass contracts and generated-CSS drift checks pass.

See [final checkpoint measurements](reference/field-final-contrast.json), [functional results](reference/field-final-functional.json), [caret results](reference/field-final-carets.json), and [contrast decision history](field-contrast-review.md). Ratios sample real screenshot pixels and composited foregrounds, not every pixel of every possible backdrop. This is not a global WCAG certification or verification of every browser, OS menu, screen reader, zoom level or consumer layout. Recheck contrast after integration.

Earlier source audits and proposals are preserved in [historical usage notes](fields-usage-history.md) and [wizard fidelity audit](wizard-field-audit.md). They do not override the current tokens. WSDMM Central/SignalView B remain unchanged; no application migrations or publishing are part of this checkpoint.
