Dostępny DatePicker w React zgodny z WCAG 2.2 AA
Zespół deweloperów oparł się na kodzie DatePicker wygenerowanym przez Claude'a według wzorca WAI-ARIA APG „Date Picker Dialog”. Pierwotny prompt zawierał wymagania WCAG 2.1/2.2 na poziomie AA: strukturę z inputem, aria-describedby, przyciskiem-triggerem, modal oknem role="dialog" i siatką tabeli kalendarza. Jednak surowa wersja ujawniła krytyczne wady w fokusie, nawigacji i dostępności.
AI stworzył podstawową nawigację klawiaturową (strzałki, Home/End, PageUp/Down, Enter/Spacja, Escape), roamingowy tabindex na gridcell, aria-live="polite" dla miesiąca. Ale testy na VoiceOver i NVDA pokazały: fokus nie trafiał na wybraną datę przy otwarciu, aria-live gromadził kolejkę komunikatów, okno dialogowe migało przy ponownym kliknięciu.
Odejścia od APG dla praktycznej dostępności
Specyfikacja APG nakazuje <td role="gridcell"> bez zagnieżdżonych przycisków, z ręcznym onClick/onKeyDown i tabindex. Praktyka wykazała zalety <button> wewnątrz <td>:
- Natywna obsługa fokusu, Enter/Spacja bez dodatkowego kodu.
- Automatyczna semantyka dla czytników ekranu.
- Obsługa disabled bez aria-disabled i preventDefault.
- Stabilne zdarzenia click z myszy/klawiatury.
To świadome odejście: specyfikacja to wskazówka, ale realne testy na urządzeniach mają priorytet. Kontrast ≥4.5:1, CSS Modules, natywny Date i Intl do formatowania — bez zewnętrznych zależności poza React/TypeScript.
Naprawa błędów funkcjonalnych i UX
Surowy kod nie kompilował się: brak index.html, błędy TypeScript. Po poprawkach wyszły na jaw:
Błędy funkcjonalne:
- Data poza minDate/maxDate przenosiła na inny miesiąc zamiast pokazywać wyłączone komórki.
- Niemożliwe wyczyszczenie inputu.
Problemy wizualne:
- Brak stylu fokusu na komórkach.
- Nierówne rozmiary td.
Dostępność:
- Brak fokusu na dacie przy otwarciu — czytnik ekranu milczy.
- aria-live="polite" na nagłówku miesiąca: kolejka komunikatów przy szybkim przełączaniu.
- Blur+click na triggerze powodował migotanie okna.
Poprawki:
- Fokus przy otwarciu na value lub dziś, czytnik odczytuje kontekst (dzień tygodnia, data, miesiąc, rok).
- aria-live="assertive" w oddzielnym ukrytym regionie dla natychmiastowego ogłoszenia miesiąca.
- Po wyborze — fokus na trigger z zaktualizowanym aria-label.
- Pokaz miesiąca value nawet z wyłączonymi dniami.
- Usunięte OK/Anuluj: Enter potwierdza, Esc zamyka.
Struktura komponentu i nawigacja klawiaturowa
Props: value (Date | null), onChange, minDate?, maxDate?, disabledDates?, locale?.
Kluczowe ARIA:
- Input: aria-describedby dla formatu, readonly.
- Trigger: dynamiczny aria-label ("Wybrano: DD.MM.RRRR" lub "Wybierz datę").
- Dialog: role="dialog", aria-modal="true", pułapka fokusu.
- Grid: table role="grid", td z button role="gridcell", aria-selected na value, aria-disabled na niedostępnych.
- Nagłówek miesiąca: aria-live="assertive" w polite-regionie.
Nawigacja:
- Strzałki: sąsiednie dni.
- Home/End: pierwszy/ostatni dzień miesiąca.
- PageUp/Down: ±1 miesiąc.
- Shift+PageUp/Down: ±1 rok.
- Enter/Spacja: wybór.
- Escape: zamknięcie.
Co najważniejsze
- AI przyspiesza szkielet (ARIA, nawigacja), ale wymaga ręcznych testów czytnikami ekranu.
- <button> w <td> jest pewniejszy niż czysty gridcell w realnych przeglądarkach+czytnikach.
- aria-live="assertive" dla nawigacji po miesiącach zapobiega kolejce.
- Zarządzanie fokusem: otwarcie na value/dziś, powrót na trigger.
- Minimalny stos: React + TS, bez bibliotek.
Komponent gotowy do produkcji jako single-date picker, z otwartym kodem do dalszego rozwoju (lata, zakresy).
— Editorial Team
Brak komentarzy.