Accessibility (A11y)

Ensure UI is operable and understandable for users with assistive technologies.

  • Build on accessible primitives (Base UI, Radix UI, or React Aria).
  • Validate keyboard navigation and focus order for every critical flow.
  • Run automated checks plus manual screen-reader passes on key journeys.

Alternatives and when to choose them

  • Strict WCAG process for regulated domains.
  • Baseline accessibility checks for internal-only tools.

Implementation checklist

  • Add semantic landmarks and heading hierarchy.
  • Verify visible focus styles.
  • Test dialogs, menus, and forms with keyboard only.

Practical examples

Icon-only button with an accessible name

<button type="button" aria-label="Open settings" className="p-2 rounded-md">
  <SettingsIcon aria-hidden="true" />
</button>

Form field with explicit label and error announcement

<label htmlFor="email">Email</label>
<input
  id="email"
  type="email"
  aria-invalid={Boolean(error)}
  aria-describedby={error ? "email-error" : undefined}
/>
{error ? (
  <p id="email-error" role="alert">
    {error}
  </p>
) : null}

Dialog with initial focus target

<Dialog>
  <DialogTrigger asChild>
    <button type="button">Delete project</button>
  </DialogTrigger>
  <DialogContent>
    <DialogTitle>Delete project?</DialogTitle>
    <p>This action cannot be undone.</p>
    <button autoFocus type="button">
      Cancel
    </button>
    <button type="button">Delete</button>
  </DialogContent>
</Dialog>

Status updates that screen readers can hear

<p aria-live="polite">{isSaving ? "Saving changes..." : "Changes saved."}</p>

Common pitfalls

  • Using visual-only state indicators.
  • Missing accessible names on icon-only controls.

On this page