/ Foundations — 03
The long version of accessibility.
axe-core catches 30–40% of WCAG violations. The other 60% comes from APG keyboard tests and real screen-reader runs. Kumiki gates merges on all three.
Three layers of testing
| What | When | Catches |
|---|---|---|
| axe-core | Every PR — LTR & RTL × every documented state | Static violations: missing labels, contrast, role validity. |
| APG keyboard | Every PR — Playwright per pattern | Tab order, arrow-key nav, Home / End / Page semantics, Escape. |
| Guidepup screen readers | Nightly schedule | What VoiceOver and NVDA actually say, in actual order. |
Required names at the type level
Where the WAI-ARIA APG mandates an accessible name (think dialogs), the requirement is enforced
by TypeScript. <Dialog.Root> won't compile without one of title, aria-label, or aria-labelledby.
<Dialog.Root title="Confirm deletion">
<!-- compiles -->
</Dialog.Root>
<Dialog.Root>
<!-- type error: missing accessible name -->
</Dialog.Root> Keyboard contracts
Each component documents its keymap on the component detail page (under the Accessibility tab). Where APG defines a pattern, Kumiki follows it verbatim — no creative interpretation.
Reduced motion, RTL, high contrast
prefers-reduced-motionshrinks all transitions to ~10 ms across the docs site.- RTL is not an afterthought. Direction-sensitive keymaps (Tabs, Slider) read direction from machine context, not the DOM.
- Forced-colors mode is honoured — components avoid background-only state hints.
The "Kumiki-ready" checklist
Every component must satisfy the checklist in docs/design/05-accessibility.md §5.6 before merge. No exceptions, no --ignore flags.