Accessibility
A sweep of every public page and every story, in light and dark, against WCAG 2.2 AA.
What it checks
yarn a11y builds the blog, docs, marketing site, and Storybook, then opens every page and story in Chromium, in both color schemes. For each one it checks:
- Roles and names — each page exposes the landmarks, headings, and labeled controls listed for it in
scripts/a11y/expectations.mjs. - axe violations — against WCAG 2.2 AA, using axe.
- Contrast — reported, but doesn't fail the run.
Known violations are recorded in scripts/a11y/baseline.json. The sweep fails on anything above the baseline, so it can't get worse.
Run part of it
yarn a11y --only docs
Surfaces are blog, docs, web-static, and storybook. The report is written to a11y-report/report.md.
Add a page
Add the route to the surface's pages in scripts/a11y/scan.mjs, then list what it must expose in expectations.mjs:
"/how-bones/get-started/installation/": [...DOCS_CHROME, h1("Installation")],
Fix something
After fixing a known violation, lower the baseline so it can't come back:
yarn a11y:update
Build an accessible component
- Name every control. A button with only an icon gets an
aria-label. A table, tab bar, or matrix takes alabelprop. - Keep focus visible. Never remove an outline without a replacement. The design system's focus ring is
--bones-shadow-focus. - Add a story in both states it matters. The sweep checks every story in light and dark, so a story is how a component gets checked.
Contrast failures are listed in the report by text and background token. They're design decisions, so they're reported, not failed.
Not covered yet
- The desktop app isn't swept.
- Keyboard navigation and screen readers are tested by hand, if at all. axe can't judge either.
Related
- Accessibility — what's covered, and what's open.
- axe and Playwright — the tools behind the sweep.
- Principles — focus and contrast rules.