Accessibility

How components handle roles, focus, keyboard, contrast and reduced motion.

Accessibility is part of each component’s definition of done. Every component page documents its strategy; this page summarises the shared rules.

Semantics

  • Native elements on web (button, input, label, h1–h6) and ARIA roles on native (role, aria-* props map to iOS/Android accessibility APIs).
  • Icons are decorative by default; icon-only buttons require aria-label at the type level.
  • Composite widgets follow the WAI-ARIA Authoring Practices (tabs, radio group, dialog, listbox).

Keyboard & focus

  • Visible 2px focus ring using the theme ring color, shown only for keyboard focus (:focus-visible).
  • Dialogs trap focus, restore it to the trigger and close on Escape.
  • Tooltips open on keyboard focus as well as hover and dismiss with Escape.

Contrast

Theme generation enforces WCAG 2.x AA (4.5:1) for every text/background pair and 3:1 for focus rings — including custom brand colors. The test suite checks all presets in light and dark mode.

Reduced motion

When the OS asks for reduced motion, CSS transitions are removed on web and components skip animations on native (useReducedMotion()). Progress indicators keep moving slowly because they convey state.

Touch targets

On iOS/Android, buttons extend their pressable area with hitSlop to reach 44pt; list items in sheets grow to 44pt on touch devices.

How we test

Unit tests query by role and accessible name (Testing Library), exercise keyboard interaction and assert ARIA state. Tests caught — and we fixed — a Tamagui Dialog.Close label override and a tooltip that did not open on keyboard focus.

Edit this page on GitHub