Skip to main content

Accessibility

Every Aidash component ships with ARIA attributes, keyboard support, and focus management. This guide covers the invariants we test against and the patterns you should follow when composing components into screens.

What we ship
Every component targets WCAG 2.1 AA. Focus rings, ARIA roles, and reduced-motion fallbacks are baked in — you don't need to remember them at every callsite.

Colour contrast

4.5:1 for body text, 3:1 for large text (18pt+ or 14pt bold). Below that, WCAG considers the text unreadable.

Aa

White on dark

15.3:1

Pass
Aa

Purple on dark

5.2:1

Pass
Aa

Gray on dark

2.1:1

Fail
Aa

Black on white

21:1

Pass
Aa

Light gray on white

2.3:1

Fail
Aa

Purple on white

9.6:1

Pass

Keyboard support

Every focusable element responds to a predictable set of keys.

KeyAction
TabMove focus to the next element
Shift + TabMove focus to the previous element
EnterActivate button, submit form, or open link
SpaceActivate button or toggle a control
EscClose dialog, dropdown, or popover
↑ ↓ ← →Navigate within a group (menu, tab, radio)

Focus rings

Aidash uses focus-visible so mouse users don't see a ring, keyboard users always do.

button.tsx
tsx
<button
  className="focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ring) focus-visible:ring-offset-2"
>
  Save
</button>
Tip
The --ring token defaults to the brand blue. Override once in your :root block to reskin every focus ring at once.

Screen reader support

ARIA is a last resort — use semantic HTML first. When you must use ARIA, respect the standard patterns.

Common patterns
tsx
{/* Icon-only button — announce a label */}
<button aria-label="Delete item">
  <TrashIcon />
</button>

{/* Loading — tell AT the state changed */}
<div role="status" aria-live="polite">
  Saving your changes...
</div>

{/* Modal — announce the dialog, trap focus */}
<div role="dialog" aria-modal="true" aria-labelledby="title" aria-describedby="desc">
  <h2 id="title">Confirm delete</h2>
  <p id="desc">This action cannot be undone.</p>
</div>

{/* Form error — associate with the field */}
<input aria-invalid="true" aria-describedby="email-error" />
<p id="email-error" role="alert">Please enter a valid email.</p>

Reduced motion

Some users get motion sickness from animations. Respect their preference.

app/globals.css
css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
Note
Aidash Components already ships this rule in globals.css. Every animation in the library respects it automatically.

Checklist

Tick each item before you ship. Click a box to remember what you've verified.

Also see