Accessibility as a System-Level Concern
Embedding accessibility into every layer of a design system — tokens, components, documentation, and governance — so it scales automatically instead of being patched on ad hoc.
8 min read
The full lesson
Accessibility fails most often not because teams are careless, but because it has no structural home in the system. It lives in post-launch audits, in checklists owned by no one, in components shipped without keyboard or screen-reader support.
A mature design system changes that. It makes accessibility a property of the infrastructure itself — built into tokens, enforced in components, tested in CI, and documented so every consumer knows exactly what they inherit and what they still owe.
This lesson walks through every layer of a design system where accessibility decisions get made, and what a system-level approach looks like at each one.
Why Individual Fixes Don’t Scale
Imagine 50 engineers consuming a design system with 80 components. If accessibility is left to each team, you effectively have 80 independent accessibility implementations — no shared contract, no shared test coverage, no shared definition of “accessible.” Bugs multiply.
The insight is this: accessibility is a quality attribute, just like performance or visual correctness. Quality attributes belong in the platform layer, not delegated to every downstream consumer.
When a button component handles focus management, keyboard activation, aria-pressed state, and minimum tap target size, every product using that button inherits those properties for free.
The flip side: the design system team must clearly document which guarantees they provide, and which concerns remain the consumer’s responsibility — for example, supplying a meaningful accessible name via a prop.
Accessibility in Design Tokens
Design tokens are where accessibility lives before any component is written. Get them right and the benefits cascade across every surface the system touches.
Contrast-safe color tokens
The modern approach encodes contrast compliance directly into your token architecture.
At the primitive tier, define a full tonal scale — for example, color-blue-100 through color-blue-900. At the semantic tier, assign roles like color-text-primary, color-text-secondary, and color-text-disabled. Choose pairings that meet WCAG 2.2 AA: 4.5:1 contrast for normal text, 3:1 for large text and UI components.
What this buys you: no engineer can accidentally pick color-blue-300 on color-blue-200, because there is no token for that pairing. Every text-on-background pairing exposed by the system has already been verified.
The outdated habit is hand-picking hex values and hoping contributors check contrast. The modern habit is making contrast a constraint at the token definition step, not a review gate.
Use WCAG 2.2 AA as your legal and baseline standard. APCA (Advanced Perceptual Contrast Algorithm) is a useful supplementary lens — especially for large text, UI components, and non-text elements — but it is not a ratified WCAG 3 requirement as of 2026. Do not cite APCA compliance as meeting a legal standard.
Spacing and sizing tokens for target size
WCAG 2.2 introduced Success Criterion 2.5.8 (Target Size, Minimum), requiring interactive targets to be at least 24x24 CSS pixels. SC 2.5.5 (AAA) sets the more ergonomic 44x44 target. Encode this directly in your sizing tokens:
$touch-target-minimum: { $value: "24px", $type: "dimension" }
$touch-target-comfortable: { $value: "44px", $type: "dimension" }
Components that consume these tokens automatically meet the criterion. Teams that hardcode height: 32px in one-off overrides opt themselves out of the guarantee.
Motion tokens and prefers-reduced-motion
Motion tokens like duration-fast, duration-moderate, and easing-spring should ship with a parallel reduced-motion theme. That theme collapses durations to near-zero and removes spring curves:
@media (prefers-reduced-motion: reduce) {
--duration-fast: 0ms;
--duration-moderate: 0ms;
--easing-spring: linear;
}
This is not optional polish. Vestibular disorders and photosensitive epilepsy make excessive animation a real harm. Baking the reduced-motion override into the token layer means every animated component in the system picks it up automatically.
Accessible Component Architecture
The accessible-by-default contract
Every interactive component in the system should ship with:
- Keyboard operability:
Tabfocuses it;EnterorSpaceactivates it. Composite widgets like listboxes, radio groups, and menus use arrow keys. - Visible focus indicator: WCAG 2.2 SC 2.4.11 and SC 2.4.7 require visible, non-hidden focus. Never use
outline: nonewithout a custom replacement that meets the 3:1 contrast requirement against adjacent colors. - ARIA semantics: role, state, and property attributes that correctly reflect the component’s current state.
- Accessible name resolution: components accept a
label,aria-label, oraria-labelledbyprop and warn in dev mode when none is provided.
Focus management in overlays
Dialogs, drawers, and tooltips all require careful focus management. The system should own this entirely, using the inert attribute — now baseline-supported — rather than legacy tabindex manipulation:
<!-- When dialog is open, background content is inert -->
<div id="page-content" inert>...</div>
<dialog open>...</dialog>
The inert attribute removes all descendants from the tab order and blocks pointer events in one declaration. It replaces focus-trap libraries that manually iterated focusable elements — cleaner, more correct, and works with shadow DOM.
Compound component patterns
For complex composites like tabs, accordions, carousels, and data tables, implement the relevant ARIA Authoring Practices Guide (APG) pattern and document it clearly. Consumers should not need to know that a tab panel requires role="tabpanel", aria-labelledby, and managed focus on activation. The component handles it. The docs describe the keyboard model.
Do
Ship components with complete ARIA role, state, and property implementation baked in. Document the keyboard interaction model in the component’s spec page. Provide a dev-mode warning when required accessible names are missing.
Don't
Document accessibility as “the consumer’s responsibility” without defining what that means. Ship interactive components with no ARIA augmentation. Leave keyboard behavior undefined and let each team implement it inconsistently.
Testing Accessibility at the System Layer
Catching issues in CI — before any product team consumes a component update — is what makes system-level accessibility real. Use three complementary layers.
Automated static analysis
- axe-core (or
@axe-core/react,jest-axe): catches roughly 30–40% of WCAG issues automatically. Run against every Storybook story in CI. - ESLint jsx-a11y: catches markup-level issues like missing
altattributes and incorrect ARIA usage at author time.
Neither replaces manual testing, but both catch regressions instantly.
Visual regression with focus states
Screenshot tests (Chromatic, Percy, Playwright visual) should capture focused states explicitly. A common regression: a CSS refactor removes the custom focus ring, and no automated test catches it because focus state requires a deliberate interaction step.
Manual and assistive-technology testing protocol
The system team should maintain a documented AT (assistive technology) testing matrix and run it before every major component release:
| Combination | Screen reader | Browser |
|---|---|---|
| macOS VoiceOver | VoiceOver | Safari |
| Windows NVDA | NVDA 2024+ | Chrome |
| Windows JAWS | JAWS 2025 | Chrome, Edge |
| iOS VoiceOver | VoiceOver | Safari |
| Android TalkBack | TalkBack | Chrome |
Testing these five combinations is the baseline. Relying only on automated checks misses the majority of real user-facing issues.
Documentation as an Accessibility Deliverable
Documentation that only describes visual appearance and props is incomplete. Every component page should include:
- Keyboard interaction table: what keys do what, with expected behavior per state.
- Screen reader behavior: what gets announced on focus, on activation, and on state change.
- Usage requirements: which props are required for accessibility, and what breaks if they are omitted.
- Known AT issues: any screen reader or browser-specific behaviors the team has documented.
- Do/Don’t examples: concrete anti-patterns like nesting interactive elements, or using an icon-only button without a label.
Living documentation co-located with code — in Storybook, for example — is the modern default because it drifts less than standalone wikis or Figma specs. When you update the component, you update the docs in the same pull request. A static PDF style guide from 2022 is not a documentation strategy.
Governance: Who Owns Accessibility in the System
Define the contract boundary
The most consequential governance decision is specifying which accessibility properties the system guarantees versus which it delegates. Write this down explicitly in your contributing guidelines.
A clear contract looks like this:
- System owns: contrast ratios for all color token pairings, keyboard operability of all interactive components, ARIA semantics and state management, minimum tap targets, reduced-motion token support, focus indicator visibility.
- Consumer owns: page-level heading structure, skip navigation links,
titleelement content, reading order in composed layouts, accessible names supplied via props, form-level error association.
Contribution gates
Pull requests that add or modify interactive components should require accessibility review as a merge gate — not an optional checklist item. That means:
- Automated axe-core tests must pass.
- A keyboard interaction walkthrough is documented.
- AT testing (at minimum VoiceOver/Safari and NVDA/Chrome) is noted in the PR.
Teams that contribute components back to the system inherit this gate. It is not bureaucratic overhead — it is the price of the guarantee the system makes downstream.
Accessibility in versioning
When a component update breaks accessibility behavior — a keyboard model change, a new required prop for accessible naming — it warrants a major version bump and a migration guide. Just like a visual breaking change.
Accessibility regressions that silently ship in a minor version violate the implicit contract with consumers who relied on the system’s guarantee.
Theming Without Losing Accessibility
Multi-theme systems — brand themes, white-label, partner customizations — introduce real risk. A custom theme’s color overrides may pass contrast in the default palette and fail in the override.
The solution is theme validation tooling. On every token override commit, run a contrast-check script against every semantic text-on-background pairing in the system. Make it a CI gate. Tools like Token Pipeline, Style Dictionary plugins, and custom scripts can all do this.
The same applies to forced-colors mode (Windows High Contrast). Use the @media (forced-colors: active) query to verify that focus indicators, borders, and interactive element boundaries remain visible — they rely on system colors that override your custom values.
@media (forced-colors: active) {
.button {
border: 2px solid ButtonText; /* Respect system color */
forced-color-adjust: none; /* Only where absolutely necessary */
}
}
Accessibility is a first-class theming concern, not an afterthought to visual fidelity.
Accessibility Metrics for Design Systems
System teams need to measure the impact of their accessibility investments. Useful metrics include:
- Automated issue rate: axe violations per component per sprint, trended over time.
- AT test coverage: percentage of components with documented AT test results.
- Consumer-reported a11y issues: accessibility-related issues filed against the system, as a percentage of total issues.
- Time to resolution for P0 a11y bugs: accessibility issues that block users with disabilities are P0. Track their resolution time separately.
- Consumer compliance rate: across product teams, what percentage are using the system’s accessibility-guaranteed component versions rather than pinned old versions or forked copies?
Tying these to a quarterly review gives the team a feedback loop and makes the accessibility investment legible to stakeholders.