Lekcja 11.4: Dostępność (Web Accessibility, ARIA)
Tworzenie stron dla wszystkich
Dostępność stron internetowych (Web Accessibility, często skracane do a11y - "a" + 11 liter + "y") oznacza projektowanie i tworzenie stron w taki sposób, aby mogły być używane przez jak najszersze grono odbiorców, w tym osoby z różnego rodzaju niepełnosprawnościami (wzroku, słuchu, ruchu, poznawczymi) oraz osoby starsze czy korzystające z urządzeń mobilnych w trudnych warunkach.
Dostępna strona to taka, która jest:
- Postrzegalna: Informacje i komponenty interfejsu muszą być prezentowane użytkownikom w sposób, który mogą postrzegać (np. alternatywy tekstowe dla obrazów, napisy dla wideo).
- Funkcjonalna: Komponenty interfejsu i nawigacja muszą być możliwe do obsłużenia (np. obsługa za pomocą klawiatury).
- Zrozumiała: Informacje i obsługa interfejsu muszą być zrozumiałe (np. czytelny język, przewidywalne działanie).
- Solidna (Rzetelna): Treść musi być wystarczająco solidna, aby mogła być wiarygodnie interpretowana przez szeroką gamę programów użytkownika, w tym technologie wspomagające (np. czytniki ekranu).
Te cztery zasady stanowią podstawę międzynarodowych wytycznych dotyczących dostępności treści internetowych - WCAG (Web Content Accessibility Guidelines).
Rola semantycznego HTML
Jak już wielokrotnie podkreślaliśmy w tym kursie, używanie semantycznych znaczników HTML (np. <nav>
, <main>
, <article>
, <button>
, <label>
) jest fundamentem dostępności. Przeglądarki i technologie wspomagające (jak czytniki ekranu używane przez osoby niewidome) rozumieją znaczenie tych elementów i mogą przekazać je użytkownikowi w odpowiedni sposób (np. poinformować, że dany element to nawigacja, przycisk czy pole formularza).
Używanie <div>
i <span>
do wszystkiego i stylizowanie ich tak, by wyglądały jak przyciski czy linki, jest złą praktyką z punktu widzenia dostępności, ponieważ te elementy nie niosą ze sobą żadnego znaczenia semantycznego.
WAI-ARIA: Gdy HTML nie wystarcza
Czasami tworzymy bardziej złożone komponenty interfejsu użytkownika (np. niestandardowe menu rozwijane, suwaki, zakładki, okna modalne), dla których standardowe elementy HTML nie mają wystarczającej semantyki lub których zachowanie jest dynamicznie zmieniane przez JavaScript. W takich sytuacjach z pomocą przychodzi WAI-ARIA (Accessible Rich Internet Applications).
ARIA to zestaw specjalnych atrybutów, które można dodać do elementów HTML, aby uzupełnić lub zmodyfikować ich semantykę i przekazać dodatkowe informacje technologiom wspomagającym. ARIA nie zmienia wyglądu ani zachowania elementu dla użytkowników widzących, ale dostarcza kluczowych informacji dla czytników ekranu i innych narzędzi.
Kluczowe koncepcje ARIA:
- Role (
role="..."
): Definiują typ lub przeznaczenie elementu, gdy standardowy znacznik HTML jest niewystarczający lub użyty niepoprawnie (np.role="button"
dla<div>
, który działa jak przycisk,role="navigation"
,role="dialog"
,role="tablist"
). - Stany (
aria-*="..."
): Opisują aktualny stan elementu, który może się zmieniać dynamicznie (np.aria-expanded="true"
dla rozwiniętego menu,aria-selected="true"
dla wybranej zakładki,aria-disabled="true"
dla nieaktywnego przycisku,aria-invalid="true"
dla pola formularza z błędem). - Właściwości (
aria-*="..."
): Definiują cechy lub relacje elementu, które zazwyczaj się nie zmieniają (np.aria-label="Zamknij okno"
dla przycisku z ikoną "X",aria-labelledby="id_naglowka"
wskazujące na element zawierający etykietę,aria-required="true"
dla wymaganego pola formularza).
Pierwsza zasada ARIA: Używaj natywnego HTML!
Najważniejszą zasadą przy stosowaniu ARIA jest: jeśli istnieje natywny element HTML lub atrybut, który ma potrzebną semantykę i zachowanie, użyj go zamiast dodawać ARIA.
Na przykład:
- Zamiast
<div role="button">
, użyj<button>
. - Zamiast
<span role="link">
, użyj<a href="...">
. - Zamiast
<div role="checkbox" aria-checked="true">
, użyj<input type="checkbox" checked>
. - Zamiast
<input type="text" aria-required="true">
, użyj<input type="text" required>
.
ARIA powinna być używana tylko wtedy, gdy natywne możliwości HTML są niewystarczające.
Przykłady popularnych atrybutów ARIA
role="..."
: Jak wspomniano, definiuje rolę elementu (np.button
,dialog
,tab
,tabpanel
,listbox
,menu
,menuitem
,progressbar
,alert
).aria-label="Tekst etykiety"
: Definiuje tekstową etykietę dla elementu, gdy nie ma widocznej etykiety tekstowej (np. dla przycisku z samą ikoną). Ta etykieta zastępuje zawartość tekstową elementu dla czytników ekranu.<button aria-label="Zamknij">×</button>
aria-labelledby="id_elementu_etykiety"
: Wskazuje ID innego elementu na stronie, który zawiera etykietę dla bieżącego elementu. Przydatne, gdy etykieta jest złożona lub znajduje się w innym miejscu.<h2 id="tytul-sekcji">Dane kontaktowe</h2> <div role="region" aria-labelledby="tytul-sekcji"> ... pola formularza ... </div>
aria-describedby="id_elementu_opisu"
: Wskazuje ID innego elementu, który zawiera dodatkowy opis lub instrukcję dla bieżącego elementu (np. opis formatu wymaganego w polu formularza).<label for="haslo">Hasło:</label> <input type="password" id="haslo" aria-describedby="opis-hasla"> <p id="opis-hasla">Hasło musi mieć co najmniej 8 znaków.</p>
aria-hidden="true"
: Ukrywa element i jego zawartość przed technologiami wspomagającymi. Przydatne dla elementów czysto dekoracyjnych lub duplikujących informacje dostępne w inny sposób. Uwaga: Nie ukrywa elementu wizualnie! Do tego służy CSS (np.display: none;
).aria-expanded="true" / "false"
: Wskazuje, czy element kontrolujący (np. przycisk menu) rozwija lub zwija inny element, oraz jaki jest jego aktualny stan.aria-controls="id_kontrolowanego_elementu"
: Wskazuje ID elementu, który jest kontrolowany przez bieżący element (np. przycisk kontroluje widoczność panelu).aria-live="polite" / "assertive"
: Używany dla regionów strony, których treść może się dynamicznie zmieniać (np. powiadomienia, wyniki wyszukiwania). Informuje czytnik ekranu, że ma ogłosić zmianę.polite
- ogłoś, gdy użytkownik będzie bezczynny;assertive
- przerwij bieżącą czynność i ogłoś natychmiast (używać ostrożnie).
Zadanie praktyczne (z rozwiązaniem)
Zadanie: Masz element <div>
, który za pomocą JavaScript działa jak przycisk przełączający widoczność innego elementu (np. panelu z dodatkowymi informacjami). Dodaj odpowiednie atrybuty ARIA, aby poprawić jego dostępność.
Rozwiązanie:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Przycisk ARIA</title>
<style>
#przycisk-toggle {
padding: 10px 15px;
border: 1px solid #007bff;
background-color: #007bff;
color: white;
cursor: pointer;
display: inline-block;
border-radius: 4px;
}
#panel-info {
border: 1px solid #ccc;
padding: 15px;
margin-top: 10px;
/* display: none; */ /* Początkowo ukryty - obsługa w JS */
}
</style>
</head>
<body>
<h1>Przełącznik panelu z ARIA</h1>
<!-- Używamy DIV jako przycisku (zła praktyka, ale na potrzeby zadania) -->
<div id="przycisk-toggle"
role="button"
tabindex="0"
aria-expanded="false"
aria-controls="panel-info">
Pokaż informacje
</div>
<div id="panel-info" style="display: none;">
<p>Tutaj znajdują się dodatkowe, ważne informacje.</p>
</div>
<script>
const przycisk = document.getElementById('przycisk-toggle');
const panel = document.getElementById('panel-info');
przycisk.addEventListener('click', togglePanel);
// Dodajemy obsługę klawiatury (Enter lub Spacja)
przycisk.addEventListener('keydown', function(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault(); // Zapobiegaj domyślnej akcji (np. scroll)
togglePanel();
}
});
function togglePanel() {
const isExpanded = przycisk.getAttribute('aria-expanded') === 'true';
// Zmień stan ARIA
przycisk.setAttribute('aria-expanded', !isExpanded);
// Zmień tekst przycisku
przycisk.textContent = isExpanded ? 'Pokaż informacje' : 'Ukryj informacje';
// Pokaż/ukryj panel
panel.style.display = isExpanded ? 'none' : 'block';
}
</script>
</body>
</html>
W tym przykładzie:
- Nadaliśmy
<div>
rolębutton
. - Dodaliśmy
tabindex="0"
, aby element był osiągalny za pomocą klawiatury (klawisz Tab). - Użyliśmy
aria-expanded
do wskazania stanu (zwinięty/rozwinięty), który jest aktualizowany przez JavaScript. - Użyliśmy
aria-controls
do wskazania ID elementu, którym steruje przycisk. - Dodaliśmy obsługę zdarzenia
keydown
dla klawiszy Enter i Spacji, co jest oczekiwanym zachowaniem dla przycisków.
Pamiętaj: Lepszym rozwiązaniem byłoby użycie natywnego elementu <button>
, który ma wbudowaną obsługę klawiatury i odpowiednią semantykę. Wtedy potrzebowalibyśmy tylko atrybutów aria-expanded
i aria-controls
.
Dodatkowe zadania do samodzielnego wykonania:
- Przebuduj powyższy przykład, używając elementu
<button>
zamiast<div>
. Zobacz, jak upraszcza się kod (nie potrzebarole
itabindex
, a obsługa klawiatury jest wbudowana). - Dodaj do formularza pole tekstowe i przycisk z ikoną (np. lupą) służący do wyszukiwania. Użyj
aria-label
, aby nadać przyciskowi dostępną nazwę "Szukaj". - Utwórz prosty system zakładek (tabs) używając ARIA roles (
tablist
,tab
,tabpanel
) i stanów (aria-selected
).
Najczęściej zadawane pytania
Co to jest dostępność stron internetowych (a11y)?
To praktyka projektowania i tworzenia stron tak, aby mogły być używane przez wszystkich, w tym osoby z niepełnosprawnościami, zgodnie z zasadami postrzegalności, funkcjonalności, zrozumiałości i solidności (WCAG).
Co to jest ARIA?
ARIA (Accessible Rich Internet Applications) to zestaw atrybutów HTML (role, stany, właściwości), które dodają lub modyfikują semantykę elementów, czyniąc je bardziej dostępnymi dla technologii wspomagających, gdy natywny HTML nie wystarcza.
Kiedy powinienem używać ARIA?
Używaj ARIA tylko wtedy, gdy nie ma odpowiedniego natywnego elementu HTML lub atrybutu, który zapewniałby wymaganą semantykę i funkcjonalność. Zawsze preferuj natywne rozwiązania HTML.
Do czego służy `role` w ARIA?
Atrybut `role` definiuje typ lub przeznaczenie elementu dla technologii wspomagających, np. `role="button"`, `role="dialog"`, `role="navigation"`. Używany, gdy sam znacznik HTML nie niesie wystarczającej informacji.
Jak opisać przycisk z samą ikoną dla czytnika ekranu?
Użyj atrybutu `aria-label="Tekst opisujący funkcję przycisku"`, np. `<button aria-label="Zamknij">×</button>`. Tekst z `aria-label` zostanie odczytany zamiast zawartości przycisku.