WCAG 2.1 AA — release blocker
Input must satisfy these on every render before it ships.
Web (React DOM)
- The wrapper carries the focus indicator via
focus-within:ring-2, so the visible focus state wraps the input and the leading/trailing icons. Neveroutline: nonewithout a replacement. aria-invalidis set wheninvalid={true}(or whenaria-invalidis passed through explicitly).aria-requiredis set whenrequired={true}.aria-describedbyis not rendered by the component — pair the input with an error message element and link them yourself. The wiring is intentional: where the message lives in the layout is a consumer concern, not the primitive's.- Color contrast on the placeholder, the border, and the destructive variant has been verified ≥ 4.5:1 against the wrapper background.
React Native
- Wrapper is a
<View>; the input is<TextInput>.accessibilityLabelis forwarded to the<TextInput>. accessibilityState.disabledmirrorseditable={false}.- For invalid state, the destructive border carries the visual signal. Pair with
accessibilityHintso screen readers announce the error after the label.
Label association
Input does not render its label. Use a <label htmlFor> (web) or rely on accessibilityLabel (native). The reason: every form layout positions labels differently — above, beside, inside the field with a float — and burying the label inside the primitive forces all of them into one position.
Reduced motion
Only the border-color transition runs (200 ms). The user agent honours prefers-reduced-motion automatically; no special handling needed.
Audit checklist
- Wrapper shows a visible focus ring on tab-in (light and dark).
-
invalid={true}setsaria-invalidand applies the destructive style in one step. -
required={true}setsaria-required. - Error message is referenced from
aria-describedbyand is announced by screen readers after the label. - Placeholder is never the only label — every input has an associated
<label>oraccessibilityLabel. - Touch target ≥ 44 px on
size="lg". - Color contrast ≥ 4.5:1 on placeholder, border, and destructive variant in both themes.