Kurs HTML i CSS · Moduł 4: Flexbox

Dostępność (Accessibility) w HTML - budowanie dla wszystkich

9 min czytania ·
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:

  1. Opisz treść, nie formę ("Kot śpiący" zamiast "Zdjęcie")
  2. Kontekst ma znaczenie - co obrazek wnosi do treści?
  3. Bądź zwięzły - 125-150 znaków max
  4. Dekoracyjne obrazki - użyj pustego alt=""
  5. Obrazki tekstowe - przepisz dokładnie tekst
  6. Złożone diagramy - użyj również aria-describedby lub <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-label opisuje funkcję
  • Focus outline dla użytkowników klawiatury
  • type="submit" - jasna funkcja

Jak testować dostępność

1. Lighthouse (Chrome DevTools)

Krok po kroku:

  1. Otwórz DevTools (F12)
  2. Zakładka "Lighthouse"
  3. Zaznacz "Accessibility"
  4. 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:

  1. Schowaj mysz
  2. 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
  3. 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> lub aria-label
  • Błędy walidacji są czytelne i jasne
  • Wymagane pola są oznaczone (required lub aria-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
  • role są poprawnie użyte
  • Dynamiczne treści mają aria-live
  • Modals mają role="dialog" i aria-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 opis
  • aria-labelledby - łączy z istniejącym elementem
  • aria-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. 1. Jaka jest złota zasada ARIA opisana w lekcji?

  2. 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:

Przydatne artykuły