Barrierefreier React-DatePicker nach WCAG 2.2 AA-Standards
Unser Entwicklerteam ist mit Claude-generiertem DatePicker-Code gestartet, basierend auf dem WAI-ARIA APG „Date Picker Dialog“-Pattern. Der anfängliche Prompt spezifizierte WCAG 2.1/2.2 Level-AA-Anforderungen: Eingabefeld mit aria-describedby, Auslöse-Button, modales Dialog mit role="dialog" und kalenderbasierter Gittertabelle. Die rohe Version zeigte jedoch kritische Probleme bei Fokus, Navigation und Barrierefreiheit.
Die KI implementierte grundlegende Tastaturnavigation (Pfeiltasten, Pos1/Ende, Bild hoch/runter, Eingabe/Leertaste, Escape), wandernden tabindex auf gridcell-Elementen und aria-live="polite" für Monatswechsel. Tests mit VoiceOver und NVDA deckten jedoch Probleme auf: Der Fokus landete beim Öffnen nicht auf dem ausgewählten Datum, aria-live-Ankündigungen stauten sich, und das Dialog flackerte bei wiederholten Klicks.
Abweichungen vom APG für Praxis-Barrierefreiheit
Die APG-Spezifikation fordert <td role="gridcell"> ohne verschachtelte Buttons, mit manuellen onClick/onKeyDown und tabindex. Praxis-Tests zeigten klare Vorteile bei <button> innerhalb von <td>:
- Native Fokusbehandlung und Unterstützung für Eingabe/Leertaste ohne Extra-Code.
- Automatische Semantik für Screenreader.
- Eingebauter deaktivierter Zustand ohne aria-disabled oder preventDefault.
- Zuverlässige Klick-Events per Maus oder Tastatur.
Das war eine bewusste Entscheidung: APG ist eine Orientierung, aber Gerätetests haben Vorrang. Wir stellten ≥4,5:1 Kontrast sicher, nutzten CSS-Module und native Date/Intl-APIs für Formatierung – keine externen Abhängigkeiten außer React/TypeScript.
Behebung funktionaler und UX-Probleme
Der Rohcode ließ sich nicht kompilieren: fehlende index.html und TypeScript-Fehler. Nach Korrekturen tauchten weitere Probleme auf:
Funktionale Bugs:
- Daten außerhalb minDate/maxDate sprangen in einen anderen Monat statt deaktivierte Zellen zu zeigen.
- Keine Möglichkeit, das Eingabefeld zu löschen.
Visuelle Probleme:
- Fehlende Fokus-Stile auf Zellen.
- Ungleiche td-Größen.
Barrierefreiheit:
- Kein Fokus auf ausgewähltem Datum beim Öffnen – Screenreader blieben stumm.
- aria-live="polite" am Monats-Header führte zu Ankündigungsstaus bei schneller Navigation.
- Blur + Klick auf Auslöser verursachte Dialog-Flackern.
Lösungen:
- Beim Öffnen landet Fokus auf Wert oder heute; Screenreader kündigt Kontext an (Wochentag, Datum, Monat, Jahr).
- aria-live="assertive" in separater versteckter Region für sofortige Monatsankündigungen.
- Nach Auswahl kehrt Fokus zum Auslöser mit aktualisiertem aria-label zurück.
- Zeige Monat des Werts auch bei deaktivierten Tagen.
- OK/Abbrechen entfernt: Eingabe bestätigt, Esc schließt.
Komponentenstruktur und Tastaturnavigation
Props: value (Date | null), onChange, minDate?, maxDate?, disabledDates?, locale?.
Wichtige ARIA:
- Eingabe: aria-describedby für Format, readonly.
- Auslöser: dynamisches aria-label („Ausgewählt: TT.MM.JJJJ“ oder „Datum wählen“).
- Dialog: role="dialog", aria-modal="true", Fokusfalle.
- Gitter: table role="grid", td mit button role="gridcell", aria-selected auf Wert, aria-disabled auf nicht verfügbare Daten.
- Monats-Header: aria-live="assertive" in polite-Region.
Navigation:
- Pfeiltasten: benachbarte Tage.
- Pos1/Ende: erster/letzter Tag des Monats.
- Bild hoch/runter: ±1 Monat.
- Umschalt+Bild hoch/runter: ±1 Jahr.
- Eingabe/Leertaste: auswählen.
- Escape: schließen.
Wichtigste Erkenntnisse
- KI beschleunigt das Gerüst (ARIA, Navigation), aber Screenreader-Tests sind essenziell.
- <button> in <td> ist in realen Browsern + Screenreadern zuverlässiger als reines gridcell.
- aria-live="assertive" verhindert Staus bei Monatsnavigation.
- Fokus-Management: Öffnen auf Wert/heute, Rückkehr zum Auslöser.
- Minimaler Stack: React + TS, keine Bibliotheken.
Die Komponente ist produktionsreif als Einzeldatum-Picker, mit offenem Code für Erweiterungen (Jahre, Bereiche).
— Editorial Team
Noch keine Kommentare.