Design System Tokens & Component Contracts

Permalink to "Design System Tokens & Component Contracts"

A design system is the single most leveraged place to do accessibility work in a data product organisation. A focus token fixed once fixes every grid, chart and form. A required label prop names every table built afterwards. A keyboard contract written once becomes documentation, help dialogs and tests for every product. Equally, a design system with an inaccessible default spreads that failure everywhere: a compact density with 14-pixel checkboxes, a focus ring that vanishes on selected rows, an optional name that nobody sets.

This topic covers the foundations that decide whether products built with a design system are accessible by default: component APIs (props), tokens (density and focus), contracts (keyboard behaviour) and the process for changing them (deprecation). The users affected are every user of every product built on the system. The people affected inside the organisation are design system maintainers, who own the foundations, and product teams, who inherit them. The failure this topic prevents is a design system that is “accessible” in its own documentation and inaccessible in production, because the accessible path required extra effort that teams did not spend.

This topic belongs to accessible data component libraries.

WCAG criteria in scope

Permalink to "WCAG criteria in scope"
Criterion Level Foundation that addresses it
1.3.1 Info and Relationships A Row header props; structured components
1.4.11 Non-text Contrast AA Focus and selection tokens
1.4.12 Text Spacing AA Density tokens with minimum heights
2.1.1 Keyboard A Keyboard contracts; deprecating mouse-only APIs
2.4.7 / 2.4.11 Focus AA Focus tokens; sticky-aware layouts
2.5.8 Target Size (Minimum) AA Density tokens with target minimums
4.1.2 Name, Role, Value A Required name props
4.1.3 Status Messages AA Message props and announcer integration

Prerequisites

Permalink to "Prerequisites"
Foundations of an accessible design system Layers: tokens at the base (colour, focus, density); components with accessibility props above them; contracts describing behaviour; tests verifying tokens, props and contracts; and the deprecation process that changes foundations safely. Foundations of an accessible design systemProcessdeprecation, versioning, adoption trackingTeststoken contrast, target size, contract tests, ARIA snapshotsContractskeyboard behaviour as data; help and docs generatedComponentsrequired names, row headers, messages, safe defaultsTokenscolour, focus, density, motion
Each layer depends on the one beneath it.

ARIA & HTML spec reference

Permalink to "ARIA & HTML spec reference"
Foundation What it encodes Example
Name props aria-label / aria-labelledby on the right element label or labelledBy required
Row header prop th scope="row" or rowheader rowHeader: 'id'
Message props role="status" content messages.sorted()
Focus tokens :focus-visible outline two-tone ring
Density tokens min heights; target minimums targetMin: 24px
Contract data keys, focus, announcements gridContract.contexts
Deprecations API lifecycle @deprecated with WCAG note

Step-by-step implementation

Permalink to "Step-by-step implementation"

Step 1 — Accessibility props

Permalink to "Step 1 — Accessibility props"
type GridProps = ({ label: string } | { labelledBy: string }) & { rowHeader: string; messages?: GridMessages };

See designing accessibility props for grid components.

Step 2 — Density tokens

Permalink to "Step 2 — Density tokens"
{ "compact": { "rowMinHeight": "32px", "targetMin": "24px" } }

See density tokens and target size in data grids.

Step 3 — Focus tokens

Permalink to "Step 3 — Focus tokens"
:focus-visible { outline: 2px solid var(--focus-inner); box-shadow: 0 0 0 6px var(--focus-outer); }

See focus ring tokens across themes.

Step 4 — Keyboard contracts

Permalink to "Step 4 — Keyboard contracts"
export const gridContract = { version: '4.2', contexts: { navigation: [ … ] } };

See documenting keyboard contracts for components.

Step 5 — Deprecations

Permalink to "Step 5 — Deprecations"
/** @deprecated since 5.2 — fails SC 2.1.1. Use rowAction. Removed in 6.0. */ onRowClick?: …

See versioning and deprecating inaccessible component APIs.

Step 6 — Test foundations in CI

Permalink to "Step 6 — Test foundations in CI"

Token contrast tests, target-size and text-spacing tests per density, contract-generated keyboard tests and ARIA snapshots per component state.

Which foundation prevents which product failure Matrix of product failures — unnamed grids, invisible focus on selected rows, tiny targets in compact mode, mouse-only row actions, outdated help dialogs — against props, density tokens, focus tokens, contracts and deprecation. Which foundation prevents which product failurePropsDensitytokensFocus tokensContractsDeprecationUnnamed gridsPreventsNoNoNoRetiresoptional nameInvisible focus on selectionNoNoPreventsNoNoTiny targets in compactNoPreventsNoNoRetirescompactbooleanMouse-only row actionsrowActionNoNoDocuments keysRetiresonRowClickOutdated help dialogsNoNoNoPreventsNo
Each foundation prevents a class of failures across every product.

Keyboard interaction contract

Permalink to "Keyboard interaction contract"

The design system’s contracts are the product’s keyboard contracts. For grids, the core contract every component should share:

Context Keys Action
Navigation Arrows, Home/End, Ctrl+Home/End, Page Up/Down Move focus
Selection Space, Shift+Space, Shift+arrows, Ctrl+A Select rows or ranges
Editing Enter or F2, Escape, Tab (in entry mode) Edit, cancel, move
Commands Shift+F10, Ctrl+C, ? Menu, copy, help

Screen reader compatibility matrix

Permalink to "Screen reader compatibility matrix"

Foundations are verified with screen readers through component tests rather than per product:

Foundation Verified how Readers
Name and row header props ARIA snapshots + manual check NVDA, JAWS, VoiceOver
Message props Speech log or manual check NVDA, VoiceOver
Focus tokens Visual tests (not screen reader) —
Density tokens Target and spacing tests —
Contracts Generated keyboard tests + manual announcements NVDA, VoiceOver

Edge cases & failure modes

Permalink to "Edge cases & failure modes"

1. Optional accessibility props

Permalink to "1. Optional accessibility props"

Diagnosis: most usages omit them. Fix: required props with codemods.

2. Tokens verified on one surface

Permalink to "2. Tokens verified on one surface"

Diagnosis: focus passes on the page, fails on selected rows. Fix: test every surface pair.

3. Density shrinking targets

Permalink to "3. Density shrinking targets"

Diagnosis: compact checkboxes at 14 px. Fix: separate target minimums.

4. Contracts in wikis

Permalink to "4. Contracts in wikis"

Diagnosis: docs drift from behaviour. Fix: contracts as data with generated tests.

5. Silent breaking changes

Permalink to "5. Silent breaking changes"

Diagnosis: required props added in a minor. Fix: warn, codemod, remove in a major.

Designing accessibility props for grid components

Permalink to "Designing accessibility props for grid components"

Required name and row header props, typed messages with i18n defaults, safe defaults and narrow escape hatches. See designing accessibility props for grid components.

rowHeader: keyof Row;

Density tokens and target size in data grids

Permalink to "Density tokens and target size in data grids"

Visual density separated from hit areas, minimum heights instead of fixed heights, every density verified. See density tokens and target size in data grids.

.cell-control { min-inline-size: var(--target); min-block-size: var(--target); }

Focus ring tokens across themes

Permalink to "Focus ring tokens across themes"

Two-tone outline rings verified against every surface, inset in dense grids, preserved in forced colours. See focus ring tokens across themes.

@media (forced-colors: active) { :focus-visible { outline-color: CanvasText; } }

Documenting keyboard contracts for components

Permalink to "Documenting keyboard contracts for components"

Contracts as data that render docs and help dialogs and generate tests, with deviations explicit. See documenting keyboard contracts for components.

{ keys: ['Shift+F10'], action: 'Open row menu', focus: 'first menu item' }

Versioning and deprecating inaccessible component APIs

Permalink to "Versioning and deprecating inaccessible component APIs"

Warn, provide replacements, migrate with codemods, measure adoption and remove in majors, citing WCAG. See versioning and deprecating inaccessible component APIs.

console.warn('[Grid] onRowClick is deprecated (keyboard users cannot activate rows). Use rowAction.');

Cross-cutting concerns

Permalink to "Cross-cutting concerns"

Documentation. Put an “Accessibility” section at the top of every component page: required props, keyboard contract, announcements, known issues. Teams read the top of pages.

Ownership. Each foundation needs an owner: tokens (design), props and contracts (engineering), tests (QA or platform), deprecations (release management).

Feedback loops. Product bugs that trace to foundations should be fixed in the design system first, then rolled out; track how many product bugs each foundation change prevents.

Multi-framework systems. Keep tokens and contracts framework-neutral (JSON) and generate framework-specific outputs from them.

A foundation fix reaching products Sequence among a product team, the design system, CI and other products: a product reports invisible focus on selected rows; the design system changes focus tokens; CI token tests verify every surface; the release reaches all products on upgrade. A foundation fix reaching productsProduct teamDesign systemCIOther productsbug: focus invisible onselectiontwo-tone focus tokenstoken tests pass on allsurfacesrelease 5.4release 5.4
One fix, every product.

Worked example: a design system accessibility year

Permalink to "Worked example: a design system accessibility year"

A design system serving 25 products spent a year on foundations rather than per-product fixes. Quarter one: required label and rowHeader props with codemods. Quarter two: density tokens with target minimums and focus tokens verified on every surface, with token tests in CI. Quarter three: keyboard contracts as data for grid, tree, menu and combobox, generating docs, help dialogs and tests. Quarter four: deprecation of onRowClick, statusColor and the compact boolean. Product-level accessibility bugs related to these areas fell by more than 70%, and new products started at a far higher baseline.

Who is affected, and how

Permalink to "Who is affected, and how"
Group Effect of strong foundations
Users of every product Consistent, accessible behaviour by default
Product teams Fewer decisions; accessible path is the easy path
Design system team Fixes once, benefits everywhere
QA Tests generated from contracts and tokens
Leadership Measurable reduction in product-level bugs

Getting started with foundations

Permalink to "Getting started with foundations"

Most design systems already have tokens and components; accessibility foundations are an evolution rather than a rebuild. Start by measuring: how many grid usages set a name, which surfaces fail focus contrast, which densities fail target size, which keys components handle without documentation. Those measurements identify the highest-value foundation changes. Then fix in this order — focus tokens (immediate visual benefit everywhere), required name props (largest screen reader benefit), density tokens (pointer users), contracts (long-term consistency) — and use the deprecation process for anything that changes APIs.

Working with product teams

Permalink to "Working with product teams"

Foundations only help if products adopt them. Publish short migration guides, run office hours during major upgrades, provide codemods for mechanical changes, and show teams their own numbers (unnamed grids, failing focus surfaces) before and after. Product teams generally welcome changes that remove work from them; the required-prop approach removes the need to remember accessibility details in every screen.

Design system integration

Permalink to "Design system integration"

This topic is itself about design system integration; the key practice is to treat accessibility foundations as part of the system’s public API. Version them, document them, test them, and change them through the same release process as any other API. Publish an accessibility page for the system as a whole listing tokens, contracts, required props, deprecations and known issues.

Common mistakes in design system foundations

Permalink to "Common mistakes in design system foundations"
  • Accessibility as a per-component checklist rather than tokens and contracts shared across components.
  • Optional props for essential semantics.
  • Tokens tested on a white page only.
  • Contracts nobody can test.
  • No deprecation path, so inaccessible APIs live forever.

Measuring foundation quality

Permalink to "Measuring foundation quality"

Track: percentage of components with required name props, number of token pairs tested per theme, contracts with generated tests, deprecated API usage remaining across repositories, and product bugs traced to foundations per quarter. These numbers show whether the system is making accessibility easier or merely documenting it.

Foundations for charts and dashboards

Permalink to "Foundations for charts and dashboards"

The same foundations apply beyond grids. Chart components benefit from required title and summary props, a table slot, palette tokens verified at 3:1 against plot backgrounds in both themes, focus tokens read by chart renderers for data point highlights, and keyboard contracts for point navigation. Dashboards benefit from layout tokens that keep widgets reflowable at 400% and from a shared announcer contract so widgets do not compete for the speech channel.

Testing checklist

Permalink to "Testing checklist"

Tokens

Permalink to "Tokens"

Components

Permalink to "Components"

Contracts and process

Permalink to "Contracts and process"

FAQ

Permalink to "FAQ"
Where should a design system start?

With required name props and focus tokens. Both are cheap and fix failures in every product.

Are tokens enough for accessible colour?

Tokens make it possible; tests make it true. Test every token pair that can appear together.

Should keyboard contracts follow the ARIA patterns exactly?

As closely as possible; document deviations with reasons.

How do we handle teams that override design system styles?

Make tokens and utilities the only supported customisation path, and review overrides for accessibility impact.

How do we get product teams to adopt new versions?

Codemods, office hours, adoption dashboards and clear accessibility benefits in release notes.

Should the design system publish an ACR?

Component-level conformance notes help products write their ACRs; products must still test as assembled.

Can foundations cover charts?

Yes: chart wrappers with required titles and summaries, palette tokens, focus tokens for data points and contracts for chart keyboard navigation.

How do we prevent regressions in foundations?

Token tests, contract tests and ARIA snapshots in the design system’s CI, run on every pull request.

How do foundations relate to WCAG conformance?

They make conformance achievable at scale; products still need testing as assembled, because layout, content and integration introduce their own issues.

What tooling helps most?

Style Dictionary or similar for tokens, TypeScript for required props, Storybook for contracts and states, and Playwright for tests.

How do we prioritise foundation work?

By how many product failures each change prevents. Focus tokens and required names usually top the list.

Should accessibility foundations be optional for teams with special needs?

Provide escape hatches, but make them explicit and reviewed. Most “special needs” turn out to be missing features in the foundation, which are better added centrally.

Permalink to "Related"

← Back to Accessible Data Component Libraries