# ADR 0017: Accessibility (WCAG 2.1 AA) as a default requirement - Status: accepted - Date: 2026-07-21 ## Context A full WCAG 2.1 AA audit (July 2026) found a solid baseline — enforced accessible names on `IconButton`, the implicitly labelling `Field` primitive, localized ARIA labels, a clean landmark structure — but also systemic gaps that had accumulated because accessibility was checked after the fact rather than built in: dialogs without focus management, a hard-coded document language, broken listbox semantics, page-level horizontal scrolling at 320 px viewport width. All findings were fixed in issues #162–#171; an axe-core smoke pack (`apps/web/e2e/a11y.spec.ts`) now pins the core screens in CI. Retro-fitting accessibility is far more expensive than building it in. This ADR makes it a default requirement for every future feature. ## Decision **Every user-facing change ships accessible.** Concretely, a feature is not done until it satisfies this checklist: 1. **Reuse the accessible primitives.** Icon-only controls go through `IconButton` (enforces a name); form controls go through `Field` (label + `aria-describedby`/`aria-invalid` wiring); dialogs use `useModalFocus` (focus trap, initial focus, focus return) plus `aria-labelledby` on their heading. Do not hand-roll parallels. 2. **Keyboard first.** Everything reachable and operable by keyboard, visible focus, Escape closes overlays, no focus loss on unmount. New single-key shortcuts must respect the "disable single-key shortcuts" setting (`lib/single-key-shortcuts.ts`). 3. **Name, role, value.** New widgets carry correct roles and localized (de+en) accessible names; state via `aria-expanded`, `aria-selected`, `aria-pressed` etc. Decorative icons stay `aria-hidden`. 4. **Both render paths.** Anything that renders content must behave identically in the editor/NodeView path AND the `docToHtml`/server path (public view, comments, previews) — including labels and list semantics. Remember: `page_content_cache` rolls out `docToHtml` changes lazily (on next persist). 5. **Color and contrast.** Use the design tokens; text ≥ 4.5:1, UI boundaries and icons ≥ 3:1 (`--color-border-input` exists for field borders; `--color-favorite` is icon-only). Color is never the only cue. Dark-rendered surfaces (public shell) keep their checked colors. 6. **Reflow.** New layouts must not force page-level horizontal scrolling at 320 px (flex/grid children need `min-width: 0`; intrinsically wide content scrolls in its own focusable container). 7. **Motion and time.** Respect `prefers-reduced-motion`; no auto-updating or auto-dismissing content faster than users can perceive it. 8. **Extend the CI pack.** A new core screen or a new interaction pattern gets added to `apps/web/e2e/a11y.spec.ts` (axe, WCAG A/AA tags, empty allowlist — additions to the allowlist need a written justification in the spec). Code review and issue acceptance criteria treat violations of this checklist like functional defects, not polish. ## Consequences - New features cost slightly more up front and no longer accumulate an accessibility backlog; the July 2026 audit stays the last big one. - The axe CI pack fails builds on regressions of the fixed state; specs may need locator updates when accessible names are added (a11y labels can make `getByLabel` matchers ambiguous — scope them). - The full audit report (methodology, per-criterion matrix, measurement harness) lives in the operator's workspace (`doku/audit-barrierefreiheit-2026-07.md`) and can be re-run after larger UI changes.