import { Meta, Canvas } from '@storybook/addon-docs/blocks'; import * as AlertStories from '../src/ui/alert/alert.stories'; import * as FormFieldStories from '../src/ui/form-field/form-field.stories'; # Accessibility No single tool catches every a11y class of bug, so this repo layers four, each catching what the ones below/above it can't. ## The layers 1. **Axe on every story** — `@storybook/addon-a11y` in the panel, plus `@storybook/test-runner` + `axe-playwright` gating CI (`npm run test-storybook:ci`). Catches structural/contrast/ARIA-shape violations on every component, automatically, as soon as a story exists. Escape hatch: `parameters: { a11y: { disable: true } }`, only with an inline justification comment + a cross-reference to the WP that will fix it (see e.g. `task-list.stories.ts`). 2. **Template a11y lint** — `angular-eslint`'s `templateAccessibility` config (`alt-text`, `label-has-associated-control`, `click`/`mouse-events-have-key-events`, `interactive-supports-focus`, `valid-aria`, `no-autofocus`, …) running on every inline template via `angular.processInlineTemplates` (this repo has no `.html` files — every template is a string in the `@Component` decorator; the processor extracts each one into a virtual file the template rules can lint). Catches missing alt text, unlabelled controls, and interactive elements that can't be reached by keyboard — at lint time, before a story even exists. 3. **Play tests** — Storybook stories assert the wiring axe/lint can't see: `form-field.stories.ts`'s canonical composition asserts `aria-describedby` joins `-desc`/`-error` in the right order; `alert.stories.ts` asserts `role="alert"` for errors vs `role="status"` for info/ok/warning. These run as part of the same `test-storybook:ci` gate as the axe checks, so a regression fails CI, not just a panel. 4. **Manual WCAG checklist** (`docs/reference/wcag-checklist.md`) — what none of the above can see: tab order across a whole page, focus traps, 200%-zoom reflow, and how a real screen reader narrates a flow. A living per-page checklist, not a one-time audit — it already caught a real bug (a dashboard alert overflowing at 320px) that no automated layer here would have flagged. ## Component wiring this protects The description (`-desc`) and error (`-error`) ids are joined in a pinned order so a screen reader announces the hint, then the error, never neither. See `text-input.component.ts`'s `describedBy()`. Errors are `role="alert"` (assertive — interrupts, because the user needs to know _now_); info/ok/warning stay `role="status"` (polite) so they don't interrupt whatever the user is doing. See `alert.component.ts`. ## Route-change focus Client-side routing has no page (re)load, so a screen reader/keyboard user's focus stays wherever it was — usually the link they just clicked, now detached from any content that matters. `shared/layout/route-focus.ts` moves focus to the new page's `