Kurs HTML i CSS · Moduł 4: Flexbox
Dostępność (Accessibility) w HTML - budowanie dla wszystkich
W tej lekcji8
Witaj w Świątyni Uniwersalnej Wiedzy! Podobnie jak starożytni Egipcjanie budowali piramidy z szerokich, dostępnych schodów dla wszystkich, którzy chcieli wejść do wnętrza, tak i my musimy budować strony internetowe dostępne dla wszystkich użytkowników - w tym osób z niepełnosprawnościami. Poznaj ARIA i semantyczne HTML - klucze do tworzenia inkluzywnego internetu!
Czym jest Accessibility (A11y)?
Accessibility (w skrócie a11y - "a" + 11 liter + "y") to zapewnienie, że strony internetowe mogą być używane przez wszystkich, niezależnie od ich możliwości fizycznych lub technicznych.
Kto korzysta z dostępnych stron:
- Osoby niewidome i słabowidzące (czytniki ekranu)
- Osoby niesłyszące (napisy, transkrypcje)
- Osoby z problemami motorycznymi (nawigacja klawiaturą)
- Osoby starsze (większe czcionki, wysoki kontrast)
- Użytkownicy wolnych połączeń (lekkie strony)
Analogia egipska: Jak hieroglify były zrozumiałe dla kapłanów, pisarzy i zwykłych ludzi na różnych poziomach, tak strona powinna być dostępna dla wszystkich użytkowników.
Semantyczne znaczniki vs div z rolami
Semantyczne HTML - pierwszy krok do dostępności
<!-- ZŁE - divs bez znaczenia -->
<div class="header">
<div class="nav">
<div class="nav-item">Home</div>
<div class="nav-item">About</div>
</div>
</div>
<div class="main-content">
<div class="article">
<div class="article-title">Tytuł artykułu</div>
<div class="article-text">Treść artykułu...</div>
</div>
</div>
<!-- DOBRE - semantyczne znaczniki -->
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<article>
<h1>Tytuł artykułu</h1>
<p>Treść artykułu...</p>
</article>
</main>Dlaczego semantyczne znaczniki są lepsze:
- Czytniki ekranu automatycznie rozpoznają strukturę
- Wyszukiwarki lepiej indeksują zawartość (SEO)
- Przeglądarki zapewniają domyślne zachowania
- Łatwiejsze utrzymanie kodu
- Nawigacja klawiaturą działa out-of-the-box
Kluczowe semantyczne znaczniki
<!-- Struktura strony -->
<header>Logo i nawigacja główna</header>
<nav>Menu nawigacyjne</nav>
<main>Główna treść strony (tylko jeden na stronie!)</main>
<article>Samodzielna jednostka treści (post bloga, artykuł)</article>
<section>Tematyczna sekcja dokumentu</section>
<aside>Treść poboczna, powiązana z główną treścią</aside>
<footer>Stopka sekcji lub strony</footer>
<!-- Zawartość tekstowa -->
<h1>, <h2>, <h3>, <h4>, <h5>, <h6>Nagłówki w hierarchii</h1>
<p>Paragraf tekstu</p>
<blockquote>Cytat</blockquote>
<figure>Ilustracja z opisem</figure>
<figcaption>Podpis do ilustracji</figcaption>
<!-- Interaktywne elementy -->
<button>Przycisk (do akcji)</button>
<a href="#">Link (do nawigacji)</a>
<form>Formularz</form>
<input>, <select>, <textarea>Pola formularza</input>ARIA - Accessible Rich Internet Applications
ARIA to zestaw atrybutów, które rozszerzają semantykę HTML i pomagają technologiom asystującym (czytniki ekranu) zrozumieć interfejs.
Złota zasada ARIA:
"Nie używaj ARIA, jeśli możesz użyć semantycznego HTML!"
ARIA Roles - definiowanie roli elementu
Role mówią czytnikowi ekranu, czym jest dany element:
<!-- Kiedy semantyczny HTML NIE wystarcza -->
<!-- Bez role - nie jest jasne, co to jest -->
<div class="navigation">
<a href="/">Home</a>
<a href="/products">Products</a>
</div>
<!-- Z role="navigation" -->
<div role="navigation">
<a href="/">Home</a>
<a href="/products">Products</a>
</div>
<!-- NAJLEPSZE - semantyczny tag (nie potrzeba role!) -->
<nav>
<a href="/">Home</a>
<a href="/products">Products</a>
</nav>Najpopularniejsze ARIA roles:
<!-- Nawigacja -->
<div role="navigation">Menu</div>
<!-- Lepiej: <nav> -->
<!-- Główna treść -->
<div role="main">Główna zawartość</div>
<!-- Lepiej: <main> -->
<!-- Przycisk -->
<div role="button" tabindex="0">Kliknij</div>
<!-- Lepiej: <button> -->
<!-- Alert -->
<div role="alert">Błąd! Wypełnij wszystkie pola.</div>
<!-- Baner (header strony) -->
<div role="banner">Logo i nawigacja</div>
<!-- Lepiej: <header> -->
<!-- Informacje dodatkowe -->
<div role="complementary">Sidebar</div>
<!-- Lepiej: <aside> -->
<!-- Treść artykułu -->
<div role="article">Post na blogu</div>
<!-- Lepiej: <article> -->
<!-- Wyszukiwarka -->
<div role="search">
<form>
<input type="search" aria-label="Szukaj w serwisie">
<button>Szukaj</button>
</form>
</div>
<!-- Tab panel -->
<div role="tabpanel" aria-labelledby="tab1">
Zawartość zakładki 1
</div>UWAGA: Jeśli używasz role na divie, musisz też zadbać o:
- Nawigację klawiaturą (
tabindex) - Obsługę zdarzeń (Enter, Space dla przycisków)
- Stany (aria-pressed, aria-expanded)
aria-label - dodanie etykiety
aria-label dodaje tekstowy opis elementu, który jest czytany przez czytniki ekranu:
<!-- Ikona bez tekstu -->
<button aria-label="Zamknij okno">
<svg><!-- Ikona X --></svg>
</button>
<!-- Link do social media -->
<a href="https://facebook.com" aria-label="Odwiedź nas na Facebooku">
<img src="fb-icon.svg" alt="">
</a>
<!-- Wyszukiwarka -->
<input
type="search"
aria-label="Wyszukaj produkty w sklepie"
placeholder="Szukaj...">
<!-- Przycisk hamburger menu -->
<button
class="hamburger"
aria-label="Otwórz menu nawigacyjne"
aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
<!-- Nawigacja stronnicowania -->
<nav aria-label="Nawigacja między stronami">
<a href="?page=1">1</a>
<a href="?page=2" aria-current="page">2</a>
<a href="?page=3">3</a>
</nav>Kiedy używać aria-label:
- Element ma tylko ikonę (brak tekstu)
- Widoczny tekst jest niejasny lub niepełny
- Kontekst nie jest oczywisty
Kiedy NIE używać aria-label:
- Element ma już jasny, widoczny tekst
- Możesz użyć
<label>w formularzu - Obrazek ma już odpowiedni
alt
aria-labelledby - powiązanie z istniejącą etykietą
aria-labelledby łączy element z innym elementem, który służy jako etykieta:
<!-- Modal dialog -->
<div role="dialog" aria-labelledby="dialog-title" aria-describedby="dialog-desc">
<h2 id="dialog-title">Potwierdź usunięcie</h2>
<p id="dialog-desc">Czy na pewno chcesz usunąć ten element? Tej operacji nie można cofnąć.</p>
<button>Usuń</button>
<button>Anuluj</button>
</div>
<!-- Tabs -->
<div role="tablist">
<button role="tab" id="tab1" aria-controls="panel1">Opis</button>
<button role="tab" id="tab2" aria-controls="panel2">Specyfikacja</button>
</div>
<div role="tabpanel" id="panel1" aria-labelledby="tab1">
Szczegółowy opis produktu...
</div>
<div role="tabpanel" id="panel2" aria-labelledby="tab2">
Specyfikacja techniczna...
</div>
<!-- Sekcja z nagłówkiem -->
<section aria-labelledby="section-heading">
<h2 id="section-heading">Najnowsze artykuły</h2>
<article>...</article>
<article>...</article>
</section>aria-describedby - dodatkowy opis
aria-describedby dodaje rozszerzony opis elementu:
<!-- Pole formularza z pomocą -->
<label for="password">Hasło</label>
<input
type="password"
id="password"
aria-describedby="password-requirements">
<p id="password-requirements">
Hasło musi zawierać minimum 8 znaków, w tym wielką literę i cyfrę.
</p>
<!-- Przycisk z dodatkowym kontekstem -->
<button aria-describedby="delete-warning">
Usuń konto
</button>
<p id="delete-warning">
Ta akcja jest nieodwracalna i usunie wszystkie Twoje dane.
</p>
<!-- Tooltip -->
<button aria-describedby="tooltip1">
Zapisz
</button>
<span id="tooltip1" role="tooltip" hidden>
Zapisz zmiany w dokumencie (Ctrl+S)
</span>Znaczenie atrybutu alt w obrazkach
Atrybut alt to jeden z najważniejszych elementów dostępności. Opisuje obrazek dla osób, które go nie widzą.
Dobre praktyki dla alt
<!-- DOBRE - opisowy alt -->
<img
src="pyramids-giza.jpg"
alt="Trzy piramidy w Gizie o zachodzie słońca, z wielbłądami na pierwszym planie">
<!-- DOBRE - funkcjonalny alt dla linku -->
<a href="/products">
<img src="shop-icon.svg" alt="Przejdź do sklepu">
</a>
<!-- DOBRE - pusty alt dla obrazków dekoracyjnych -->
<img src="decorative-line.svg" alt="">
<!-- ZŁE - alt identyczny jak nazwa pliku -->
<img src="img_001.jpg" alt="img_001">
<!-- ZŁE - niepotrzebne "zdjęcie", "obrazek" -->
<img src="cat.jpg" alt="Zdjęcie przedstawiające kota">
<!-- Lepiej: alt="Kot śpiący na kanapie" -->
<!-- ZŁE - brak alt -->
<img src="important-chart.jpg">
<!-- Czytnik ekranu przeczyta nazwę pliku! -->Zasady pisania dobrych alt:
- Opisz treść, nie formę ("Kot śpiący" zamiast "Zdjęcie")
- Kontekst ma znaczenie - co obrazek wnosi do treści?
- Bądź zwięzły - 125-150 znaków max
- Dekoracyjne obrazki - użyj pustego
alt="" - Obrazki tekstowe - przepisz dokładnie tekst
- Złożone diagramy - użyj również
aria-describedbylub<figcaption>
<!-- Obrazek dekoracyjny (CSS background byłby lepszy) -->
<img src="divider.svg" alt="">
<!-- Obrazek z tekstem -->
<img src="sale-banner.jpg" alt="Wyprzedaż 50% - tylko dziś!">
<!-- Złożony wykres -->
<figure>
<img
src="sales-chart.jpg"
alt="Wykres sprzedaży 2024"
aria-describedby="chart-description">
<figcaption id="chart-description">
Wykres pokazuje wzrost sprzedaży z 10 000 w styczniu do 50 000 w grudniu 2024.
Największy skok nastąpił w listopadzie podczas Black Friday.
</figcaption>
</figure>Przykład: dostępny vs niedostępny komponent
Niedostępny przycisk
<div class="button" onclick="submitForm()">
<img src="send-icon.svg">
</div>
<style>
.button {
background: blue;
padding: 10px;
cursor: pointer;
}
</style>Problemy:
- Nie ma semantyki (div zamiast button)
- Nie działa z klawiatury (Tab, Enter)
- Brak tekstowego opisu (tylko ikona)
- Czytnik ekranu nie wie, że to przycisk
Dostępny przycisk
<button
type="submit"
aria-label="Wyślij formularz">
<img src="send-icon.svg" alt="">
</button>
<style>
button {
background: blue;
padding: 10px;
cursor: pointer;
border: none;
color: white;
}
button:focus {
outline: 3px solid orange;
outline-offset: 2px;
}
</style>Zalety:
- Semantyczny
<button>- automatyczna obsługa klawiatury aria-labelopisuje funkcję- Focus outline dla użytkowników klawiatury
type="submit"- jasna funkcja
Jak testować dostępność
1. Lighthouse (Chrome DevTools)
Krok po kroku:
- Otwórz DevTools (F12)
- Zakładka "Lighthouse"
- Zaznacz "Accessibility"
- Kliknij "Generate report"
Lighthouse sprawdzi:
- Kontrast kolorów
- Atrybuty alt w obrazkach
- ARIA attributes
- Hierarchię nagłówków
- Form labels
- I wiele więcej...
Wynik: Raport z oceną 0-100 i konkretnymi problemami do naprawienia.
2. Nawigacja klawiaturą
Test ręczny:
- Schowaj mysz
- Używaj tylko klawiatury:
- Tab - przejdź do kolejnego elementu
- Shift+Tab - wróć do poprzedniego
- Enter - aktywuj link/przycisk
- Space - zaznacz checkbox, aktywuj przycisk
- Arrow keys - nawigacja w select, radio buttons
- Sprawdź:
- Czy widzisz focus outline?
- Czy kolejność jest logiczna?
- Czy możesz dotrzeć wszędzie?
- Czy nie ma "pułapek" na focus?
3. Czytnik ekranu
Windows: NVDA (darmowy) Mac: VoiceOver (wbudowany - Cmd+F5) Chrome: ChromeVox (rozszerzenie)
Podstawowe komendy (NVDA):
- Ctrl - zatrzymaj czytanie
- Insert+Down Arrow - czytaj wszystko
- H - skocz do kolejnego nagłówka
- K - skocz do kolejnego linku
- B - skocz do kolejnego przycisku
- F - skocz do kolejnego pola formularza
4. axe DevTools (rozszerzenie)
Rozszerzenie do Chrome/Firefox, które automatycznie wykrywa problemy z dostępnością:
- Instalacja: Chrome Web Store → "axe DevTools"
- Użycie: F12 → zakładka "axe DevTools" → "Scan ALL"
5. WAVE (WebAIM)
Online narzędzie: https://wave.webaim.org/
- Wprowadź URL strony
- Otrzymasz wizualną analizę z oznaczonymi błędami
Checklist dostępności
HTML i semantyka:
- Używam semantycznych znaczników (
<header>,<nav>,<main>,<article>) - Hierarchia nagłówków jest logiczna (h1 → h2 → h3)
- Każda strona ma dokładnie jeden
<main>
Obrazki:
- Wszystkie obrazki mają atrybut
alt - Obrazki dekoracyjne mają pusty
alt="" - Alt teksty są opisowe i zwięzłe
Formularze:
- Każde pole ma
<label>lubaria-label - Błędy walidacji są czytelne i jasne
- Wymagane pola są oznaczone (
requiredlubaria-required="true")
Nawigacja klawiaturą:
- Mogę dotrzeć wszędzie używając tylko Tab
- Focus outline jest widoczny
- Kolejność Tab jest logiczna
- Interaktywne elementy reagują na Enter/Space
Kolory i kontrast:
- Kontrast tekstu to minimum 4.5:1 (normalny tekst)
- Kontrast tekstu to minimum 3:1 (duży tekst 18px+)
- Informacje nie są przekazywane TYLKO kolorem
ARIA:
- ARIA używane tylko gdy semantyczny HTML nie wystarcza
rolesą poprawnie użyte- Dynamiczne treści mają
aria-live - Modals mają
role="dialog"iaria-labelledby
Testy:
- Lighthouse Accessibility: 90+
- Przetestowane czytnikiem ekranu
- Przetestowane tylko klawiaturą
Podsumowanie
Semantyczne HTML:
- Używaj
<button>,<nav>,<main>,<article>zamiast divów - Zapewnia automatyczną dostępność
- Lepsza dla SEO i maintenance
ARIA roles:
role="navigation",role="main",role="button"- Używaj TYLKO gdy semantyczny HTML nie wystarcza
- Wymaga dodatkowej obsługi klawiatury i stanów
ARIA labels:
aria-label- dodaje tekstowy opisaria-labelledby- łączy z istniejącym elementemaria-describedby- dodaje rozszerzony opis
Alt w obrazkach:
- Opisuj treść, nie formę
- Pusty
alt=""dla dekoracji - Kontekst ma znaczenie
Testowanie:
- Lighthouse (DevTools)
- Nawigacja klawiaturą
- Czytnik ekranu (NVDA, VoiceOver)
- axe DevTools
- WAVE
Pamiętaj: Dostępność to nie dodatkowa funkcja - to fundamentalna część tworzenia stron internetowych. Podobnie jak piramidy w Gizie były budowane dla wszystkich pokoleń, tak strony internetowe powinny być dostępne dla wszystkich użytkowników!
Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jaka jest złota zasada ARIA opisana w lekcji?
2. Kiedy najlepiej użyć atrybutu aria-label?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj semantyczne znaczniki w typowej strukturze strony od góry do dołu: