Technika · WAI‑ARIA 1.2 · W3C
ARIA — co to jest i kiedy jej nie używać
ARIA (Accessible Rich Internet Applications) to zestaw atrybutów HTML, które mówią technologiom wspomagającym, czym jest element, w jakim jest stanie i jak się nazywa — kiedy sam HTML tego nie mówi. Jest niezbędna do dostępnych komponentów budowanych z div‑ów. Jest też najczęstszym źródłem błędów w audytach, bo źle użyta psuje więcej, niż naprawia. Ustawa nie wspomina o ARIA; wymaga „kompatybilności” z narzędziami wspomagającymi (art. 5 pkt 12), a WCAG 4.1.2 mierzy ją właśnie przez nazwy, role i stany.
Drzewo dostępności
Przeglądarka buduje z DOM drugie drzewo — drzewo dostępności — i to je czyta czytnik ekranu. Każdy węzeł ma rolę (przycisk, link, nagłówek, pole), nazwę (co czytnik przeczyta), stan (rozwinięty, zaznaczony, wyłączony) i wartość. Element <button>Zapłać</button> ma rolę button i nazwę „Zapłać” za darmo. Element <div class="btn">Zapłać</div> ma rolę generic i dla czytnika jest zwykłym tekstem: nie da się do niego dojść Tabem ani go nacisnąć Enterem. ARIA pozwala mu nadać rolę — ale nie daje zachowania klawiatury; to trzeba dopisać w JavaScript.
Pięć zasad ARIA
Nie używaj ARIA, jeśli HTML wystarczy
<button>zamiast<div role="button" tabindex="0">.<nav>,<main>,<details>,<dialog>,<select>mają wbudowane role, stany i klawiaturę. Natywny element działa w każdej kombinacji przeglądarki i czytnika; ARIA — w większości.Nie zmieniaj natywnej semantyki bez potrzeby
<h2 role="tab">zabiera nagłówek.<a role="button">jest OK tylko, gdy link naprawdę działa jak przycisk i obsługuje spację.Każdy interaktywny element ARIA działa z klawiatury
Rola button bez obsługi Enter i spacji to kłamstwo wobec użytkownika: czytnik powie „przycisk”, a naciśnięcie nic nie zrobi.
Nie ukrywaj fokusowalnych elementów
role="presentation"lubaria-hidden="true"na czymś, co da się sfokusować, tworzy „ducha”: Tab tam trafia, czytnik milczy. Nasz analizator wykrywa to jako aria-hidden-focusable i aria-presentation-interactive.Każdy interaktywny element ma dostępną nazwę
Ikona‑przycisk bez tekstu potrzebuje
aria-labelalbo ukrytego wizualnie tekstu. „Przycisk” bez nazwy to najczęstszy błąd w koszykach (ikony ±, kosz, lupa, hamburger).
Trzy rodzaje atrybutów
| Rodzaj | Przykłady | Do czego |
|---|---|---|
| Role | role="button", dialog, tablist/tab/tabpanel, navigation, alert, status, combobox, listbox, menu | mówią, czym element jest; dozwolone role są zamkniętą listą — wymyślona rola (role="card") jest błędem (aria-role-invalid) |
| Właściwości | aria-label, aria-labelledby, aria-describedby, aria-required, aria-controls, aria-haspopup, aria-live | opisują relacje i cechy; labelledby/describedby/controls wskazują id — nieistniejące id to aria-ref-broken |
| Stany | aria-expanded, aria-selected, aria-checked, aria-pressed, aria-disabled, aria-invalid, aria-current, aria-hidden | zmieniają się w czasie i muszą być aktualizowane przez skrypt; stan, który się nie zmienia, jest gorszy od braku stanu |
Nazwa dostępna — skąd się bierze
Algorytm nazwy (Accessible Name Computation) bierze pierwszą rzecz, która istnieje: aria-labelledby → aria-label → natywna etykieta (<label for>, alt, <caption>) → treść tekstowa → title. Skutki, które zaskakują: aria-label nadpisuje widoczny tekst (przycisk „Kup teraz” z aria-label="submit" jest dla czytnika „submit”, a sterowanie głosem „kliknij Kup teraz” nie działa — WCAG 2.5.3); placeholder nie jest etykietą; title jest ostatnią deską ratunku i nie działa dotykiem. Nasz analizator liczy nazwę tym algorytmem dla każdego linku, przycisku i pola.
Regiony na żywo
aria-live="polite" (ogłoś, gdy skończę mówić) i aria-live="assertive" (przerwij i ogłoś) oraz role status i alert sprawiają, że czytnik ekranu dowie się o zmianie, której nie widzi: „dodano do koszyka”, „3 wyniki”, „błąd: podaj e‑mail”. Bez tego użytkownik klika „Dodaj do koszyka” i nie wie, czy coś się stało (WCAG 4.1.3). Zasady: region musi istnieć w DOM przed zmianą treści; nie używaj assertive do wszystkiego; nie ogłaszaj co sekundę.
Wzorce, które trzeba znać (WAI‑ARIA Authoring Practices)
Menu rozwijane w nawigacji
Zwykle nie potrzebuje roli menu — to wzorzec dla menu aplikacji, nie nawigacji strony. Wystarczy: przycisk z aria-expanded i aria-controls, lista linków, zamykanie Esc, fokus wraca na przycisk. Tak działa menu na tej stronie (<details> + skrypt).
Zakładki (tabs)
role="tablist" na kontenerze, role="tab" z aria-selected i aria-controls na każdym przycisku, role="tabpanel" z aria-labelledby; strzałki przełączają zakładki, Tab wchodzi do panelu. Tylko aktywna zakładka ma tabindex="0".
Okno modalne
Najlepiej natywny <dialog> z showModal(): fokus w środku, Esc zamyka, tło nieaktywne. Własny: role="dialog", aria-modal="true", aria-labelledby na tytuł, pułapka fokusu w środku, powrót fokusu na element otwierający. Cookie‑banner, który nie jest modalem, nie może udawać modalu.
Akordeon
Natywny <details>/<summary> wystarcza w większości przypadków (tak jak tutaj). Własny: przycisk z aria-expanded w nagłówku, panel z id wskazanym przez aria-controls.
Wyszukiwarka z podpowiedziami (combobox)
role="combobox" na polu, aria-expanded, aria-controls na listę role="listbox" z role="option", aria-activedescendant wskazuje podświetloną opcję; strzałki poruszają, Enter wybiera, Esc zamyka. Najtrudniejszy wzorzec — jeśli można, natywny <datalist>.
Własny select / dropdown
Zanim zbudujesz: natywny <select> da się dziś stylować w wystarczającym stopniu. Jeśli musisz: przycisk z aria-haspopup="listbox" i aria-expanded, lista role="listbox", opcje role="option" z aria-selected, pełna klawiatura, wpisywanie liter przeskakuje do opcji.
Karuzela
role="region" z aria-roledescription="karuzela", przyciski poprzedni/następny/pauza z nazwami, slajdy jako role="group" z aria-roledescription="slajd" i aria-label="2 z 5", brak auto‑przewijania albo pauza dostępna jako pierwsza (WCAG 2.2.2).
Komunikaty walidacji
Pole z aria-invalid="true" i aria-describedby wskazującym tekst błędu; podsumowanie błędów w role="alert" na górze formularza z linkami do pól; fokus na pierwszym błędnym polu po wysłaniu.
Błędy, które ARIA pogarsza
aria-hidden="true"na całym<body>oprócz modalu — i pozostawione po zamknięciu.role="button"na linku prowadzącym do innej strony — użytkownik spodziewa się akcji, dostaje nawigację (i odwrotnie).aria-labelw innym języku niż strona albo z instrukcją zamiast nazwy („kliknij, aby otworzyć menu główne strony”).aria-livena całym kontenerze wyników — czytnik czyta wszystko przy każdej zmianie.tabindex="1",tabindex="5"— dodatnie wartości przestawiają kolejność fokusu (tabindex-positive); używaj tylko 0 i −1.role="presentation"na tabeli danych, żeby „wyłączyć” style — usuwa nagłówki dla czytnika.- Nakładki „accessibility widget”, które wstrzykują ARIA skryptem, nie znając struktury strony.
Jak sprawdzić
Narzędzia deweloperskie każdej przeglądarki mają zakładkę „Accessibility” z drzewem dostępności: kliknij element i zobacz jego rolę, nazwę i stany tak, jak widzi je czytnik. Potem NVDA lub VoiceOver: włącz, zamknij oczy, kup coś. Nasz analizator wykonuje w przeglądarce część tej pracy: sprawdza role z zamkniętej listy, referencje id, elementy ukryte a fokusowalne, nazwy przycisków i linków — i pokazuje, gdzie w kodzie są problemy.
