dorfteich/docs/architecture/adr/0017-accessibility-by-default.md
Claude Fable 5 858909564e
All checks were successful
CD / Build and push images (push) Successful in 1m12s
CD / Deploy to Test (push) Successful in 11s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m45s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m15s
CI / Import/export fidelity gate (push) Successful in 56s
Release / Build release images and notes (push) Successful in 1m11s
Release / Release-candidate operations QA (push) Successful in 44s
Prod deploy / Deploy the released images to Prod (push) Successful in 16s
docs: ADR 0017 — Barrierefreiheit als Standard-Anforderung
Jede künftige UI-Änderung entwickelt Barrierefreiheit direkt mit
(Stefans Vorgabe nach Abschluss des WCAG-2.1-AA-Audits): verbindliche
Checkliste als ADR, Kurzfassung in CLAUDE.md für jede Dev-Session,
Eintrag im ADR-Index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGM8jo3hwoV9wsCVGfy8iq
2026-07-21 17:17:26 +02:00

3.6 KiB
Raw Blame History

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.