Accessibility
Accessibility is not a feature — it's a quality bar. HTML is the accessible baseline. Browsers provide accessibility for free if you use semantic elements.
Semantic HTML first
If you use a <div> for a button, you have to rebuild 20 years of browser engineering yourself. Use the right element and get keyboard, focus, and screen reader support for free.
Use native elements
Native elements carry semantics, keyboard behaviour, and ARIA roles automatically.
<button onClick={handleSave}>Save</button><a href="/settings">Settings</a><nav aria-label="Main">…</nav><main>…</main>
Do
Use <button> for actions, <a> for navigation, <nav>, <main>, <article> for landmarks.
<div onClick={handleSave} className="btn">Save</div><span onClick={() => navigate('/settings')}>Settings</span>
Don’t
Use <div> or <span> with click handlers to create custom buttons or links. You lose keyboard support, focus management, and screen reader announcements.
Meaningful document structure
Heading hierarchy is the table of contents for screen reader users.
Do
Use headings in order (<h1> → <h2> → <h3>). Don't skip levels. Every page should have exactly one <h1>.
Don’t
Choose heading levels based on visual size. Use <h4> after <h2> because it looks right — use text utility classes for visual sizing instead.
ARIA as a last resort
The first rule of ARIA is don't use ARIA — if a native HTML element with the semantics you need exists, use it. ARIA supplements HTML; it doesn't replace it.
Prefer native over ARIA
ARIA overrides native semantics. Used incorrectly, it makes things worse, not better.
<!-- Native checkbox — keyboard, focus, state for free --><input type="checkbox" id="terms" /><label htmlFor="terms">I agree to the terms</label>
Do
Reach for ARIA only when there is no native element for the pattern (e.g., tabs, tree views, comboboxes). Roadie's Base UI components handle ARIA attributes automatically for these cases.
Don’t
Build custom ARIA widgets when a native element exists. Every attribute you add manually is an attribute you must maintain.
Color and contrast
Sufficient contrast is non-negotiable. Never convey meaning through colour alone. See Colors for the full colour system.
Meet WCAG AA contrast ratios
4.5:1 for normal text. 3:1 for large text (18px bold / 24px regular) and UI components.
Do
Use Roadie's semantic text colours (text-normal, text-subtle, text-strong) which are designed to meet contrast ratios in both light and dark modes.
Don’t
Use raw colour values without checking contrast. Light grey text on a white background may look subtle but fails WCAG.
Colour is not the only signal
Colour-blind users cannot distinguish red from green. Always pair colour with another indicator.
Do
Pair colour with icons, text labels, or patterns. An error state uses intent-danger (red) plus an error icon plus error text.
Don’t
Indicate success or failure only through green/red colouring with no supporting text or icon.
Keyboard and focus
Keyboard accessibility is covered in depth in the Interactions foundation — including keyboard operability, visible focus indicators, focus management, and hit targets.
Reduced motion
Motion can cause nausea and disorientation. Roadie includes a global prefers-reduced-motion reset. See Motion for full details on duration tokens, easing, and how to test.
Screen readers
Screen readers interpret the DOM, not the pixels. What you see is not what they hear.
Meaningful alt text
Images without alt text are invisible to screen readers. Decorative images need empty alt.
<!-- Informative image --><img src="seating-map.png" alt="Venue seating map showing sections A through F" /><!-- Decorative image --><img src="divider.svg" alt="" />
Do
Write alt text that describes the content or function of the image, not its appearance. Use empty alt="" for purely decorative images.
Don’t
Omit the alt attribute entirely (screen readers will read the filename) or write “image” as alt text.
Announce dynamic content
Screen readers don’t automatically notice DOM changes. Tell them what changed.
Do
Use aria-live="polite" for non-urgent updates (status messages, search results). Use role="alert" for urgent notifications (errors, warnings).
Don’t
Update content silently. A toast notification that isn't announced leaves screen reader users unaware of what happened.
Forms
Form interaction patterns (labels, placeholders, validation, submit behaviour) are covered in Interactions. This section covers the ARIA-specific attributes that make forms accessible to assistive technology.
Associate errors with fields
Screen readers need a programmatic link between an input and its error message.
Do
Use aria-describedby to link error messages to their field. Use aria-invalid to indicate the field has an error.
Don’t
Show a red border and error text next to a field without any programmatic association — sighted users see it, screen reader users don't.
Group related fields
Field groups need a shared label so screen readers announce the context.
Do
Use <fieldset> and <legend> to group related fields. Screen readers announce the legend before each field in the group.
Don’t
Group fields under a visual heading with no programmatic relationship. Screen reader users won't know the fields are related.
What Roadie handles
The design system provides accessible primitives. You get these for free when using Roadie components.
Base UI primitives
Components built on Base UI provide correct ARIA attributes, focus management, and keyboard navigation out of the box.
Focus rings
is-interactive provides visible :focus-visible rings coloured by the nearest intent.
Field state styling
is-interactive-field provides visual state transitions for focus and aria-invalid automatically.
Contrast-safe tokens
Semantic colour tokens (text-normal, text-subtle) are designed to maintain contrast ratios in both light and dark modes.
Testing checklist
Run through this checklist before shipping. Automated tools catch about 30% of issues — the rest requires manual testing.
| Test | How | What to check |
|---|---|---|
| Keyboard | Tab through the page | Every interactive element reachable and operable. Focus order is logical. No keyboard traps. |
| Screen reader | VoiceOver (macOS) or NVDA (Windows) | Content is announced meaningfully. Dynamic updates are heard. Form errors are associated. |
| Contrast | Browser DevTools or contrast checker | All text meets WCAG AA. UI components meet 3:1. |
| Reduced motion | DevTools → Rendering → Emulate prefers-reduced-motion | No animations play. No functionality is lost. |
| Zoom | Browser zoom to 200% | No content is clipped or overlapping. Layout reflows gracefully. |