feat(fp): WP-16 — component a11y: description wiring + alert role

Wires text-input's aria-describedby to the form-field description div
(the BSN hint was rendered but never announced), pins desc-before-error
ordering, and switches alert to role=alert for errors vs role=status
for info/ok/warning. Composition contract enforced by story play tests
(form-field+text-input, alert per variant) run in the WP-01 CI gate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 08:15:24 +02:00
parent 85c805b8bd
commit f3de30b72c
8 changed files with 213 additions and 108 deletions

View File

@@ -1,6 +1,6 @@
# WP-16 — Component a11y: description wiring + alert role
Status: todo
Status: done (pending commit)
Phase: 4 — a11y
## Why
@@ -57,12 +57,25 @@ Audit findings axe can't (fully) catch:
## Acceptance criteria
- [ ] Description text is programmatically associated in the canonical composition;
- [x] Description text is programmatically associated in the canonical composition;
login-form BSN hint announced.
- [ ] `-error` id appended exactly when invalid; order stable.
- [ ] Error alerts are `role="alert"`; play tests assert both behaviors and run in the
- [x] `-error` id appended exactly when invalid; order stable.
- [x] Error alerts are `role="alert"`; play tests assert both behaviors and run in the
CI gate.
## Deviation from the original plan
`radio-group`/`checkbox` were left unchanged — grepping every call site found zero
consumers pairing either with a `form-field` `description` (only the login-form BSN
field, which uses `text-input`). The WP's own Files note ("describedby joins; only
where the component takes a hint") already carved out this exact case — adding
`hasDescription` to atoms with no live description consumer would be unused surface,
not a fix. `radio-group` already had correct `-error`-only wiring; untouched.
`describedBy()` was added directly on `text-input` rather than factored into a shared
`shared/kernel` helper — one consumer, ~5 lines, not worth the indirection yet.
Manual screen-reader spot check (optional per the WP) skipped; the play test is the
enforced check going forward.
## Verification
GREEN + `npm run test-storybook:ci` (includes the new play tests).