Accessibility
Accessibility is a first-class concern in Wire UI. Every primitive ships with the correct ARIA attributes, keyboard behavior, and focus management — so you can build inclusive interfaces without wiring up semantics from scratch.
Because Wire UI ships zero CSS, the responsibility split is different from styled libraries: Wire UI handles the behavior, you handle the visuals. This page explains both sides of that contract.
Standards we target
Wire UI is built against:
- WAI-ARIA Authoring Practices — role, state, and keyboard patterns for each widget
- WCAG 2.1 / 2.2 Level AA — the industry baseline for web accessibility
- Semantic HTML first — ARIA is only added when native elements can’t express the needed semantics
What Wire UI provides
data-* attributes so you can style focus, hover, active, and disabled states with plain CSS.asChild polymorphism lets you swap any component’s root element without losing accessibility behavior.What you provide
[data-focus-visible] so keyboard users always see where focus is.aria-label.[data-disabled] states — disabled should look disabled.prefers-reduced-motion on any animations or transitions you add.Keyboard navigation
Every interactive Wire UI component supports keyboard operation. Here are the conventions used across the library:
| Key | Typical action |
|---|---|
| Tab / Shift + Tab | Move focus to the next / previous focusable element |
| Enter / Space | Activate a button, trigger, or selectable item |
| Escape | Close an overlay (Modal, Drawer, Dropdown, Tooltip) |
| Arrow keys | Navigate between items in menus, radio groups, tabs, and OTP |
| Home / End | Jump to the first / last item in a list-like widget |
Component-specific keys (for example, typeahead in Select) are documented on each component’s page under Keyboard Interactions.
Focus management
Focus traps
Modal and Drawer automatically trap focus while open — Tab cycles through focusable elements inside the overlay and can’t escape to the page behind it. When the overlay closes, focus returns to the element that triggered it.
You can customize the entry/exit points with the initialFocus and finalFocus props. See the Modal documentation for details.
Focus-visible styling
Wire UI exposes a data-focus-visible attribute that mirrors the CSS :focus-visible pseudo-class. Style it so keyboard users always see a focus indicator — this is required for WCAG 2.4.7 compliance.
<Button
className="
rounded-[8px] border border-black px-4 py-2
[data-focus-visible]:ring-2
[data-focus-visible]:ring-black
[data-focus-visible]:ring-offset-2
"
>
Click me
</Button>The focus ring appears on keyboard navigation but not on mouse click — which matches user expectation and avoids visual noise.
Accessible labels
Interactive components must have an accessible name. Wire UI passes through all standard HTML labeling attributes, so you can use the approach that best fits your UI.
Icon-only buttons
Buttons without visible text need an explicit aria-label:
<Button aria-label="Close dialog">
<CloseIcon />
</Button>Form fields
Use a native <label> element with htmlFor pointing to the input’s id:
<label htmlFor="email">Email address</label>
<Input.Root id="email" type="email" />Overlays
Modal, Drawer, and Dropdown accept aria-labelledby and aria-describedby to connect to their title and description elements. See each overlay’s docs for the recommended compound pattern.
Color contrast
Because Wire UI ships no colors, meeting contrast requirements is entirely your responsibility. Target:
- 4.5:1 for body text (WCAG AA)
- 3:1 for large text (18pt+ or 14pt+ bold) and UI components
- 3:1 for focus indicators against their background
If you’re evaluating newer standards, the APCA algorithm — being considered for WCAG 3 — gives more perceptually accurate readings than WCAG 2’s luminance math, especially for dark mode.
Useful tools: browser devtools contrast pickers, Figma contrast plugins, or dedicated tools like Stark and Colour Contrast Analyser.
Reduced motion
Wire UI primitives don’t ship any animations. When you add transitions, gate them behind the user’s motion preference:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}Or use Tailwind’s motion-safe: / motion-reduce: variants:
<Modal.Content className="motion-safe:transition-all motion-safe:duration-200">
...
</Modal.Content>Testing
Wire UI is tested against the major screen readers:
- NVDA (Windows)
- VoiceOver (macOS / iOS)
- TalkBack (Android)
For your own applications, combine automated and manual testing:
- axe-core or Lighthouse catches common issues automatically
- Keyboard-only navigation — unplug your mouse and work through a full user flow
- Screen reader spot-checks on critical paths (sign-in, checkout, forms)
- Zoom to 200% to verify text stays readable and nothing gets clipped
Component-specific details
Every component documentation page has its own Accessibility and Keyboard Interactions section with specifics for that widget. For the most complex a11y patterns, see: