ReferenceEmphasis and states tokens
Accessibility is not a feature. It's a quality bar. HTML is the accessible baseline, and browsers provide accessibility for free if you use semantic elements.
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.
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> and <main>
for landmarks, and <article> for self-contained content.
<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.
Heading hierarchy is the table of contents for screen reader users.
Do
Use headings in order, <h1>, then <h2>, then <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 the text-display-* classes for visual sizing
instead.
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, and it doesn't replace it.
ARIA overrides native semantics. Used incorrectly, it makes things worse, not better.
<!-- Native checkbox gets keyboard, focus and 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, such as tabs, tree views, and 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.
Sufficient contrast is non-negotiable. Never convey meaning through colour alone. See Colors for the full colour system.
Roadie measures all contrast with APCA, not WCAG 2 ratios. The thresholds for body text, labels, large text, and non-text UI are in Colors.
Do
Use Roadie's semantic text colours (text-normal, text-subtle,
text-strong), and check the one you pick against
the threshold for its role and surface.
Don’t
Use raw colour values without checking contrast. Light grey text on a white background may look subtle but fails APCA.
Many colour-blind users can't tell 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 or red colouring with no supporting text or icon.
Keyboard accessibility is covered in depth in the Interactions foundation. It covers keyboard operability, visible focus indicators, focus management, and hit targets.
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 interpret the DOM, not the pixels. What you see is not what they hear.
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, so screen readers read the filename, or
write "image" as alt text.
Screen readers don't automatically notice DOM changes. Tell them what changed.
Do
Use aria-live="polite" for non-urgent updates, such as status messages
and search results. Use role="alert" for urgent notifications, such as
errors and warnings.
Don’t
Update content silently. A toast notification that isn't announced leaves screen reader users unaware of what happened.
Form interaction patterns, such as labels, placeholders, validation, and
submit behaviour, are covered in Interactions
and Forms. This section covers the attributes that make
forms accessible to assistive technology, which Field and Fieldset set for
you.
Screen readers need a programmatic link between an input and its error message.
<Field invalid={hasError}><Field.Label>Email</Field.Label><Field.Input type='email' /><Field.ErrorText>Enter a valid email address</Field.ErrorText></Field>
Do
Wrap the control in Field and set invalid. Field marks the control
aria-invalid, links Field.ErrorText to it with aria-describedby, and
announces the 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.
Field groups need a shared label so screen readers announce the context.
Do
Use Fieldset and Fieldset.Legend, which render <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.
The design system provides accessible primitives. You get these for free when you use Roadie components.
| Primitive | What you get |
|---|---|
| Base UI primitives | Components built on Base UI provide correct ARIA attributes, focus management, and keyboard navigation out of the box. |
| Focus rings | The base styles draw a :focus-visible ring on links and buttons, coloured by the nearest intent. is-interactive animates it in. |
| 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) adapt to light and dark modes. Check each against the APCA threshold for its role and surface. |
Run through this checklist before shipping. Automated tools catch about 30% of issues, and the rest need manual testing.
| Test | How | What to check |
|---|---|---|
| Keyboard | Tab through the page | Every interactive element is reachable and operable. Focus order is logical. There are no keyboard traps. |
| Screen reader | VoiceOver (macOS) or NVDA (Windows) | Content is announced meaningfully. Dynamic updates are heard. Form errors are associated. |
| Contrast | An APCA contrast checker | Text, labels, and UI components meet the APCA thresholds in Colors. |
| Reduced motion | In DevTools, open Rendering and 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. |