fix(a11y): static accessibility findings (v1-readiness #4 — code-side)

Statische audit op happy-path-code; 4 categorieën gefixt vóór de Lighthouse-
verificatie die de gebruiker handmatig draait:

1. <main>-landmark op /login en /register (waren <div>); auth-pages krijgen
   nu een correcte landmark zodat screen-readers ze kunnen overslaan/nav

2. solo-task-card.tsx: agent-status-pill had role="button" + aria-label maar
   GEEN tabIndex en GEEN onKeyDown — keyboard-onbereikbaar. Nu compleet:
   tabIndex={0} + Enter/Space-handler

3. Form-label-associaties via htmlFor + id-pairs:
   - story-dialog (5): code, title, description, acceptance + priority via labelledby
   - task-dialog (3): title, description, implementation_plan
   - todo-list PromotePbi/PromoteStory dialogs (6): title, product, pbi, priority

   Lighthouse a11y "form-field-multiple-labels" en "label" rules worden
   hierdoor groen.

Niet aangeraakt:
- pbi-dialog: htmlFor was al goed gewired
- auth-form: htmlFor was al goed gewired
- Color-contrast: gebruikt MD3-tokens; theoretisch correct (verifieer in
  Lighthouse run)
- Heading-hierarchy: nog niet gescand — kan in vervolgronde

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 13:58:34 +02:00
parent 43778e3bcb
commit 31ff70b71a
6 changed files with 42 additions and 25 deletions

View file

@ -220,10 +220,11 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
{/* Title */}
<div>
<label className="text-sm font-medium mb-2 block">
<label htmlFor="task-title" className="text-sm font-medium mb-2 block">
Titel <span className="text-destructive">*</span>
</label>
<Input
id="task-title"
{...form.register('title')}
aria-invalid={!!form.formState.errors.title}
autoFocus
@ -240,13 +241,14 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
{/* Description */}
<div>
<label className="text-sm font-medium mb-2 block">Omschrijving</label>
<label htmlFor="task-description" className="text-sm font-medium mb-2 block">Omschrijving</label>
<Controller
control={form.control}
name="description"
render={({ field }) => (
<>
<TextareaAutosize
id="task-description"
{...field}
value={field.value ?? ''}
aria-invalid={!!form.formState.errors.description}
@ -275,13 +277,14 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
{/* Implementation plan */}
<div>
<label className="text-sm font-medium mb-2 block">Implementatieplan</label>
<label htmlFor="task-implementation-plan" className="text-sm font-medium mb-2 block">Implementatieplan</label>
<Controller
control={form.control}
name="implementation_plan"
render={({ field }) => (
<>
<TextareaAutosize
id="task-implementation-plan"
{...field}
value={field.value ?? ''}
aria-invalid={!!form.formState.errors.implementation_plan}