Role ARIA wyjaśnione: kiedy i jak używać ARIA w HTML

14 min read

ARIA (Accessible Rich Internet Applications) daje deweloperom potężny zestaw narzędzi do udostępniania dynamicznych, złożonych interfejsów webowych użytkownikom czytników ekranu — ale jej nadużywanie jest powszechne i kosztowne. Ten przewodnik omawia każdą główną kategorię ról ARIA, wyjaśnia złote zasady korzystania z ARIA i pokazuje konkretne przykłady kodu, abyś mógł stosować ją poprawnie.

Oto trzeźwiąca liczba: według analizy WebAIM dotyczącej milionu najpopularniejszych stron głównych serwisów internetowych, strony zawierające atrybuty ARIA mają średnio znacząco więcej wykrywalnych błędów dostępności niż strony bez ARIA. Nie jest to argument przeciwko używaniu ARIA — to argument za używaniem jej prawidłowo. ARIA jest jednym z najpotężniejszych narzędzi we współczesnym zestawie do zapewniania dostępności, ale jednocześnie jednym z najbardziej niezrozumianych. Jeśli użyjesz jej poprawnie, realnie otwierasz swoją stronę dla milionów użytkowników z niepełnosprawnościami. Jeśli użyjesz jej źle, aktywnie pogarszasz ich doświadczenie.

Czym jest ARIA i dlaczego istnieje?

ARIA to skrót od Accessible Rich Internet Applications. Jest to zestaw atrybutów HTML, zdefiniowany przez Web Accessibility Initiative w ramach W3C, który pozwala deweloperom przekazywać informacje semantyczne technologiom asystującym, takim jak czytniki ekranu, linijki brajlowskie czy oprogramowanie do sterowania głosem. Gdy przeglądarka renderuje stronę, buduje dwie równoległe struktury: DOM (to, co widzisz) oraz Drzewo Dostępności (to, co odczytują technologie asystujące). Atrybuty ARIA pozwalają modyfikować to Drzewo Dostępności tak, aby dokładnie opisać, czym są niestandardowe komponenty i jak się zachowują.

Potrzeba ARIA wynika z realnego problemu. HTML został zaprojektowany dla dokumentów, a nie aplikacji. Gdy sieć ewoluowała w platformę dla bogatych, interaktywnych doświadczeń — interfejsów z zakładkami, okien modalnych, przeciągania i upuszczania, strumieni danych na żywo — natywne elementy HTML nie były w stanie przekazać czytnikowi ekranu, czym są te komponenty ani jak działają. ARIA wypełnia tę lukę. Jak ujmuje to MDN, ARIA „uzupełnia HTML, tak aby interakcje i widżety powszechnie używane w aplikacjach mogły być przekazywane technologiom asystującym, gdy w przeciwnym razie nie ma do tego mechanizmu”.

ARIA nie zmienia prezentacji wizualnej. Nie dodaje zachowania. Nie zapewnia automatycznie obsługi klawiatury. Modyfikuje wyłącznie to, co Drzewo Dostępności ujawnia technologii asystującej. To ważne rozróżnienie — i jedno z tych, które powoduje wiele błędów popełnianych przez deweloperów, gdy sięgają po ARIA jako skrót.

Specyfikacja jest utrzymywana przez W3C jako WAI-ARIA, obecnie w wersji 1.2, przy aktywnie rozwijanej wersji 1.3. Dostarcza ontologię ról, stanów i właściwości, które razem opisują dostępne elementy interfejsu użytkownika w całym spektrum współczesnych wzorców webowych.

Trzy filary: role, stany i właściwości

Zanim napiszesz choć jedną linię ARIA, musisz zrozumieć trzy odrębne klocki konstrukcyjne, które zapewnia specyfikacja. Nie są one wymienne, a mylenie ich jest jednym z najczęstszych źródeł błędów.

Role definiują, czym element jest. Rola odpowiada na pytanie: z jakim typem rzeczy mam do czynienia? Przykłady to button, dialog, navigation, tablist i progressbar. Rolę stosujesz za pomocą atrybutu role: <div role='button'>. Rola komunikuje technologiom asystującym przeznaczenie elementu, aby użytkownik wiedział, jak z nim wchodzić w interakcję.

Stany opisują dynamiczny stan elementu — coś, co zmienia się, gdy użytkownik wchodzi w interakcję ze stroną. Atrybut aria-expanded informuje czytnik ekranu, czy sekcja rozwijana jest otwarta, czy zamknięta. aria-checked odzwierciedla, czy niestandardowe pole wyboru jest zaznaczone. Stany muszą być utrzymywane w synchronizacji z JavaScriptem; statyczne aria-expanded='false', które nigdy się nie zmienia, jest nie tylko bezużyteczne, ale wręcz wprowadza w błąd.

Właściwości dostarczają opisowych, zwykle bardziej stabilnych informacji o elemencie. aria-label nadaje elementowi nazwę dostępną, która nadpisuje jego widoczny tekst. aria-labelledby wskazuje inny element, którego tekst pełni rolę etykiety. aria-describedby łączy z dodatkowym tekstem opisowym. aria-required sygnalizuje, że pole formularza musi zostać wypełnione. Podczas gdy stany mają się często zmieniać, właściwości zwykle ustawia się raz i pozostawia — choć są wyjątki.

Role definiują, czym element jest. Stany definiują, jak zachowuje się w danej chwili. Właściwości dostarczają dodatkowego kontekstu opisowego. Potrzebujesz wszystkich trzech, współdziałających, aby stworzyć w pełni dostępny niestandardowy komponent.

Złota zasada — i dlaczego ma większe znaczenie, niż myślisz

Pierwsza zasada W3C dotycząca użycia ARIA jest jednoznaczna: jeśli możesz użyć natywnego elementu HTML lub atrybutu z wbudowaną semantyką i zachowaniem, których potrzebujesz, użyj ich. Nie sięgaj w pierwszej kolejności po ARIA. Czasem nazywa się to zasadą „brak ARIA jest lepszy niż zła ARIA” — frazą odzwierciedlającą bardzo realne zagrożenie wynikające z dobrze intencjonowanego, ale niepoprawnego użycia ARIA.

Natywne elementy HTML niosą ze sobą domyślną semantykę ARIA za darmo. Element <button> jest już eksponowany w Drzewie Dostępności jako przycisk. Jest już dostępny z klawiatury. Już reaguje zarówno na Enter, jak i Spację. Już ogłasza swoją etykietę. W momencie, gdy piszesz <div role='button'>, bierzesz na siebie odpowiedzialność za ręczne odtworzenie całego tego zachowania — obsługi klawiatury, zarządzania fokusem, aktualizacji stanów — w JavaScripcie. Nie jest to problem teoretyczny. Zapomnienie o obsłudze klawiatury w niestandardowym przycisku jest jednym z najczęstszych i najbardziej szkodliwych błędów ARIA w środowisku produkcyjnym.

Przypadki, w których ARIA jest naprawdę konieczna, zwykle skupiają się wokół kilku scenariuszy: gdy tworzysz złożony widżet, który nie ma odpowiednika w HTML (karuzela, combobox z autouzupełnianiem, widok drzewa); gdy naprawiasz zastany kod, w którym przebudowa DOM jest zbyt kosztowna; gdy tworzysz komponent webowy, który musi ujawniać niestandardową semantykę; lub gdy wsparcie przeglądarek i technologii asystujących dla natywnego elementu jest na tyle niespójne, że odpowiednik ARIA działa w praktyce bardziej niezawodnie.

Poza tymi scenariuszami twoim pierwszym odruchem powinien być zawsze semantyczny HTML. Używaj <nav> zamiast <div role='navigation'>. Używaj <main> zamiast <div role='main'>. Używaj <button> zamiast <div role='button'>. Natywne elementy są bardziej odporne, lepiej wspierane i wymagają znacznie mniej utrzymania.

Przegląd głównych kategorii ról ARIA

Specyfikacja WAI-ARIA organizuje role w kilka kategorii. Zrozumienie tych kategorii pomaga wiedzieć, po którą rolę sięgnąć i kiedy.

Role orientacyjne (landmark)

Role orientacyjne oznaczają główne regiony strony, pozwalając użytkownikom czytników ekranu przeskakiwać bezpośrednio do kluczowych sekcji za pomocą skrótów klawiaturowych. Najczęściej używane role orientacyjne to banner, navigation, main, complementary, contentinfo, search i form. Każda z nich ma bezpośredni natywny odpowiednik w HTML: <header>, <nav>, <main>, <aside>, <footer> i tak dalej. W praktyce oznacza to, że role orientacyjne są prawie zawsze zbędne, jeśli używasz nowoczesnego, semantycznego HTML. Dodawaj je tylko wtedy, gdy z powodów strukturalnych jesteś skazany na niesemantyczny kod.

<!-- Preferuj to -->
<header>
  <nav>...</nav>
</header>
<main>...</main>
<footer>...</footer>

<!-- Używaj ARIA tylko wtedy, gdy musisz użyć divów -->
<div role='banner'>
  <div role='navigation'>...</div>
</div>
<div role='main'>...</div>
<div role='contentinfo'>...</div>

Role widżetów

Role widżetów opisują interaktywne komponenty, którymi użytkownik manipuluje bezpośrednio. To tutaj ARIA wykonuje swoją najważniejszą pracę, ponieważ wiele wzorców widżetów nie ma natywnego odpowiednika w HTML. Typowe role widżetów to button, checkbox, dialog, menu, menuitem, slider, tablist, tab, tabpanel, tooltip, tree i combobox.

Gdy używasz roli widżetu, bierzesz pełną odpowiedzialność za interakcję z klawiaturą. WAI-ARIA Authoring Practices Guide (APG) definiuje oczekiwane wzorce klawiaturowe dla każdego typu widżetu — na przykład klawisze strzałek do poruszania się między zakładkami, Escape do zamykania okna dialogowego, Home i End do przechodzenia do pierwszego i ostatniego elementu w listboxie. Brak implementacji tych wzorców oznacza, że twój komponent jest technicznie oznaczony, ale funkcjonalnie bezużyteczny dla użytkowników korzystających wyłącznie z klawiatury.

<!-- Niestandardowy interfejs zakładek -->
<div role='tablist' aria-label='Account settings'>
  <button role='tab' aria-selected='true' aria-controls='panel-profile' id='tab-profile'>
    Profile
  </button>
  <button role='tab' aria-selected='false' aria-controls='panel-security' id='tab-security' tabindex='-1'>
    Security
  </button>
</div>
<div role='tabpanel' id='panel-profile' aria-labelledby='tab-profile'>
  <p>Profile settings content</p>
</div>
<div role='tabpanel' id='panel-security' aria-labelledby='tab-security' hidden>
  <p>Security settings content</p>
</div>

Role regionów dynamicznych (live region)

Regiony dynamiczne są jedną z najbardziej użytecznych funkcji ARIA. Pozwalają technologiom asystującym ogłaszać dynamiczne aktualizacje treści — takie jak komunikaty o stanie, powiadomienia o błędach, wiadomości na czacie czy wskaźniki ładowania — użytkownikom, którzy nie widzą zmiany na ekranie. Bez regionów dynamicznych użytkownik czytnika ekranu wysyłający formularz mógłby nigdy nie dowiedzieć się, czy operacja się powiodła, czy nie, o ile fokus nie zostanie explicite przeniesiony do wyniku.

Podstawowe role regionów dynamicznych to alert, status, log, marquee i timer. Rola alert niesie domyślne ustawienie aria-live='assertive', co oznacza, że natychmiast przerywa użytkownikowi — odpowiednie dla błędów lub pilnych ostrzeżeń. Rola status używa aria-live='polite', czekając, aż użytkownik skończy bieżące zadanie, zanim ogłosi komunikat — idealne dla komunikatów o powodzeniu i wskaźników postępu.

<!-- Grzeczny komunikat o stanie dla niepilnych informacji zwrotnych -->
<div role='status' aria-live='polite' aria-atomic='true'>
  <!-- Dynamicznie wstrzykuj tekst tutaj za pomocą JavaScript -->
</div>

<!-- Stanowczy alert dla błędów wymagających natychmiastowej uwagi -->
<div role='alert'>
  Please correct the errors below before submitting.
</div>

Kluczem do regionów dynamicznych jest to, że kontener musi być obecny w DOM, zanim dynamiczna treść zostanie wstrzyknięta. Region dynamiczny, który jest tworzony i wypełniany jednocześnie, jest często pomijany przez czytniki ekranu. Zbuduj kontener przy ładowaniu strony i wypełniaj go za pomocą JavaScript w miarę występowania zdarzeń.

Role struktury dokumentu

Role struktury dokumentu — takie jak article, list, listitem, table, row, cell, figure i heading — opisują strukturalną organizację treści. Większość z nich jest obecnie wypierana przez natywne elementy HTML, a MDN zauważa, że większość ról struktury dokumentu „nie powinna być już używana, ponieważ przeglądarki obsługują teraz semantyczne elementy HTML o tym samym znaczeniu”. Głównym wyjątkiem jest sytuacja, gdy pracujesz z niestandardowymi środowiskami renderowania, komponentami webowymi lub treściami opartymi na SVG, gdzie natywne elementy HTML są niedostępne.

Kluczowe właściwości ARIA, które każdy deweloper powinien znać

Poza rolami, kilka właściwości ARIA pojawia się nieustannie w pracy nad dostępnością w realnych projektach. To te, po które będziesz sięgać najczęściej:

  • aria-label: Zapewnia nazwę dostępną dla elementu, gdy nie ma dostępnej widocznej etykiety tekstowej lub gdy widoczny tekst jest niewystarczający. Typowe przypadki użycia: przyciski składające się wyłącznie z ikony, pola wyszukiwania bez widocznej etykiety, przyciski zamykania w oknach modalnych. Zwróć uwagę, że aria-label nadpisuje każdy widoczny tekst lub natywną etykietę, więc używaj go ostrożnie w elementach, które mają widoczny tekst.
  • aria-labelledby: Wskazuje jeden lub więcej elementów, których treść tekstowa pełni rolę nazwy dostępnej. Bardziej odporne rozwiązanie niż aria-label w złożonych przypadkach, ponieważ tekst etykiety pozostaje zsynchronizowany z widoczną treścią. Akceptuje listę identyfikatorów elementów rozdzielonych spacjami, a technologie asystujące łączą odwołane teksty w podanej kolejności.
  • aria-describedby: Łączy z dodatkowym tekstem opisowym — nie nazwą, lecz kontekstem. Używaj go, aby powiązać pola formularza z ich komunikatami o błędach lub skojarzyć podpowiedź (tooltip) z elementem, który opisuje. Czytniki ekranu zazwyczaj ogłaszają to po nazwie i roli elementu.
  • aria-hidden: Całkowicie usuwa element z Drzewa Dostępności. Niezastąpione dla dekoracyjnych ikon, zduplikowanej treści i elementów wyłącznie wizualnych, które tworzyłyby szum dla użytkowników czytników ekranu. Nigdy nie stosuj aria-hidden='true' do elementu, który może otrzymać fokus — użytkownik nadal może do niego dojść tabulatorem, ale nie otrzyma o nim żadnej informacji.
  • aria-expanded: Komunikuje, czy element rozwijany — rozwijane menu, akordeon lub widżet typu disclosure — jest obecnie otwarty, czy zamknięty. Musi być przełączany dynamicznie w JavaScripcie; statyczna wartość jest gorsza niż całkowity brak tego atrybutu.
  • aria-current: Wskazuje bieżący element w zbiorze, najczęściej używany w nawigacji do oznaczenia aktywnego linku do strony (aria-current='page') lub bieżącego kroku w procesie wieloetapowym.

Typowe błędy ARIA, które faktycznie szkodzą dostępności

Biorąc pod uwagę, że strony z ARIA wykazują tendencję do posiadania większej liczby błędów dostępności niż te bez niej, warto jasno powiedzieć, co najczęściej idzie nie tak. To nie są przypadki brzegowe — to wzorce, które pojawiają się w kodzie produkcyjnym każdego dnia.

Używanie ról ARIA na elementach o silnej natywnej semantyce. Niektóre elementy HTML mają to, co specyfikacja nazywa „silną natywną semantyką” — znaczenia głęboko wbudowane w przeglądarkę, których nie można bezpiecznie nadpisać. Umieszczenie nieodpowiedniej roli na <button> lub <input> może spowodować, że przeglądarka całkowicie zignoruje rolę ARIA lub wytworzy sprzeczne zachowanie, które zdezorientuje technologie asystujące. Rola, którą deklarujesz, musi być odpowiednia dla elementu, do którego ją przypisujesz.

Zapominanie o obsłudze klawiatury przy rolach widżetów. role='button' w ARIA mówi czytnikowi ekranu, że element jest przyciskiem. Nie sprawia, że element staje się obsługiwalny z klawiatury. Jeśli używasz <div> z role='button', musisz dodać tabindex='0', aby uczynić go fokusowalnym, oraz dodać nasłuchiwacze zdarzeń dla klawiszy Enter i Spacja. Brak któregokolwiek z tych elementów psuje doświadczenie użytkowników korzystających wyłącznie z klawiatury.

<!-- Niekompletne i niedostępne -->
<div role='button' onclick='doSomething()'>Submit</div>

<!-- Poprawna implementacja niestandardowego przycisku -->
<div
  role='button'
  tabindex='0'
  onclick='doSomething()'
  onkeydown='if(event.key==="Enter"||event.key===" ")doSomething()'
>Submit</div>

<!-- Albo właściwa odpowiedź: po prostu użyj button -->
<button onclick='doSomething()'>Submit</button>

Używanie aria-hidden na elementach fokusowalnych. Zastosowanie aria-hidden='true' do elementu, który może otrzymać fokus, ukrywa go w Drzewie Dostępności, ale nie przed nawigacją klawiaturą. Użytkownik klawiatury nadal może do niego dojść tabulatorem, nie otrzyma o nim żadnej informacji i nie będzie miał sposobu, by dowiedzieć się, co robi. Jest to naruszenie WCAG 2.1 w ramach Kryterium Sukcesu 4.1.2 (Nazwa, rola, wartość).

Nieaktualne stany ARIA. Brak aktualizacji aria-expanded, aria-checked, aria-selected i podobnych stanów, gdy interfejs się zmienia, pozostawia użytkowników czytników ekranu z zasadniczo nieprawidłowym obrazem interfejsu. Menu, które wizualnie się otwiera, ale którego wyzwalacz nadal ma aria-expanded='false', jest aktywnie zwodnicze.

Zbędne role. Dodanie role='navigation' do elementu <nav> lub role='button' do <button> nie robi nic pożytecznego. Zaśmieca kod i może okazjonalnie dezorientować niektóre kombinacje technologii asystujących. Zaufaj natywnej semantyce.

ARIA i WCAG: zrozumienie powiązania

ARIA to nie WCAG. To odrębne specyfikacje, które współdziałają. WCAG (Web Content Accessibility Guidelines) definiuje co — rezultaty wymagane dla dostępnej treści. ARIA jest częścią jak — technicznym mechanizmem osiągania niektórych z tych rezultatów. Najbardziej istotnym kryterium sukcesu WCAG dla ARIA jest 4.1.2: Nazwa, rola, wartość, które wymaga, aby wszystkie komponenty interfejsu użytkownika miały nazwę, ujawniały swoją rolę technologiom asystującym oraz programowo komunikowały swój stan i właściwości. ARIA jest jednym z głównych narzędzi do spełnienia tego kryterium w przypadku niestandardowych komponentów.

ARIA wspiera także kilka innych kryteriów sukcesu. Role orientacyjne przyczyniają się do 2.4.1 (Ominięcie bloków) poprzez umożliwienie pomijania nawigacji. Regiony dynamiczne są często właściwym narzędziem do spełnienia 4.1.3 (Komunikaty o stanie) w WCAG 2.1, które wymaga, aby komunikaty o stanie były programowo rozpoznawalne bez otrzymywania fokusu. Prawidłowe użycie aria-label i aria-labelledby pomaga spełnić 2.4.6 (Nagłówki i etykiety) oraz 1.3.1 (Informacje i relacje).

Warto zauważyć, że zgodność z WCAG staje się coraz częściej wymogiem prawnym. European Accessibility Act wszedł w pełni w życie w czerwcu 2025 r., rozszerzając obowiązkowe wymagania dostępności na szeroką gamę prywatnych usług cyfrowych w państwach członkowskich UE. W Stanach Zjednoczonych ADA jest coraz częściej interpretowana jako obejmująca dostępność stron internetowych, a wymagania federalne wynikające z Section 508 dotyczą instytucji rządowych i organizacji finansowanych ze środków federalnych. Prawidłowe zrozumienie ARIA to nie tylko dobra praktyka — to coraz częściej część twoich obowiązków związanych z zgodnością.

Testowanie implementacji ARIA

Jedynym sposobem, aby wiedzieć, czy twoja implementacja ARIA faktycznie działa, jest przetestowanie jej z użyciem realnych technologii asystujących. Narzędzia automatyczne, takie jak axe, WAVE i Lighthouse, potrafią wychwycić naruszenia strukturalne — brak wymaganej właściwości, nieprawidłową rolę, aria-hidden zastosowane do elementu fokusowalnego — ale nie powiedzą ci, czy czytnik ekranu ogłasza twoje okno modalne w sposób sensowny ani czy nawigacja klawiaturą w twoim niestandardowym widżecie drzewa odpowiada oczekiwanym wzorcom.

Do testów manualnych główne czytniki ekranu, które należy objąć, to JAWS i NVDA w systemie Windows (razem reprezentują zdecydowaną większość użycia czytników ekranu na desktopie) oraz VoiceOver w systemach macOS i iOS. TalkBack obejmuje Androida. Każda kombinacja czytnika ekranu i przeglądarki może zachowywać się inaczej, więc zdecydowanie zaleca się testowanie co najmniej dwóch kombinacji. Przetestuj każdy stan interaktywny: otwórz okno dialogowe, rozwiń akordeon, wybierz opcję, wywołaj alert. Upewnij się, że komunikat odpowiada temu, co użytkownik widzący zrozumiałby, patrząc na ten sam interfejs.

Testując niestandardowe widżety, przejdź przez model interakcji klawiaturą zdefiniowany w WAI-ARIA Authoring Practices Guide dla danego typu widżetu. Jeśli twój tablist nie reaguje na klawisze strzałek albo twoje okno dialogowe nie przechwytuje fokusu, są to błędy niezależnie od tego, jak poprawnie wygląda znacznik ARIA w automatycznym audycie.

Najważniejsze wnioski

  • Zawsze preferuj semantyczny HTML zamiast ARIA. Natywne elementy, takie jak <button>, <nav>, <main> i <dialog>, mają wbudowaną semantykę dostępności, która jest bardziej odporna i wymaga znacznie mniej kodu niż ich odpowiedniki z ARIA na divach. Sięgaj po ARIA tylko wtedy, gdy natywny HTML naprawdę nie wystarcza.
  • Role ARIA to obietnica, a nie skrót. Zastosowanie role='button' lub role='dialog' do niestandardowego elementu zobowiązuje cię do zaimplementowania pełnego modelu interakcji klawiaturą dla danego typu widżetu. Role bez odpowiadającego im zachowania tworzą zamieszanie i prowadzą do naruszeń WCAG.
  • Utrzymuj stany ARIA w synchronizacji z interfejsem. Dynamiczne atrybuty, takie jak aria-expanded, aria-checked, aria-selected i treści aria-live, muszą być aktualizowane w JavaScripcie wraz ze zmianami w UI. Nieaktualny stan jest aktywnie szkodliwy — komunikuje użytkownikowi błędne informacje.
  • Używaj regionów dynamicznych dla aktualizacji treści. Każda treść, która aktualizuje się bez przeładowania strony — powiadomienia, komunikaty o błędach, stany ładowania, strumienie czatu — potrzebuje regionu aria-live lub odpowiedniej roli, takiej jak alert lub status, aby użytkownicy czytników ekranu otrzymywali te same informacje, które użytkownicy widzący widzą automatycznie.
  • Testuj z użyciem realnych technologii asystujących, a nie tylko narzędzi automatycznych. Skanery automatyczne wychwytują strukturalne błędy ARIA, ale nie są w stanie zweryfikować, czy twoja implementacja zapewnia spójne, użyteczne doświadczenie. Testy manualne z JAWS, NVDA i VoiceOver to jedyny sposób, aby zamknąć tę lukę.