Kurs HTML i CSS · Moduł 10: Dostępność (a11y)
Nawigacja klawiaturowa
W tej lekcji8
Odłóż na chwilę myszkę i spróbuj kupić bilet do muzeum samym klawiszem Tab. Jeśli po kilku naciśnięciach nie wiesz, gdzie jesteś, albo przycisk "Kup" w ogóle nie reaguje, właśnie poczułeś, jak wygląda internet dla wielu osób. W Starożytnym Egipcie drogi i korytarze świątyń były precyzyjnie zaplanowane, aby każdy pielgrzym mógł dotrzeć do celu. Podobnie strony internetowe muszą zapewniać jasne ścieżki nawigacji dla użytkowników, którzy nie korzystają z myszy - nawigują wyłącznie za pomocą klawiatury.
Dlaczego nawigacja klawiaturowa jest ważna?
Wiele osób korzysta ze strony tylko za pomocą klawiatury:
- Osoby z ograniczoną sprawnością motoryczną
- Użytkownicy czytników ekranowych
- Zaawansowani użytkownicy (programiści, power users)
- Osoby z tymczasowymi urazami (np. złamaną ręką)
Co więcej, wiele technologii wspomagających, takich jak sterowanie głosem czy przełączniki, działa, symulując naciśnięcia klawiszy. Strona sprawna z klawiatury działa więc także z nimi.
Podstawowe klawisze nawigacji
| Klawisz | Funkcja |
|---|---|
Tab | Przejście do następnego elementu interaktywnego |
Shift + Tab | Powrót do poprzedniego elementu |
Enter | Aktywacja linku lub przycisku |
Spacja | Aktywacja przycisku, zaznaczenie checkboxa |
Strzałki | Nawigacja w menu, radio buttons, tablist |
Escape | Zamknięcie okna dialogowego, anulowanie |
Tab odwiedza elementy w kolejności, w jakiej stoją w kodzie HTML, a nie w kolejności, w jakiej widać je na ekranie. Dlatego na typowej stronie focus trafia kolejno na skip link, linki w nawigacji, elementy interaktywne w treści głównej i na końcu na linki w stopce.
Focus i tabindex
Naturalny focus
Focus to jak pochodnia w ciemnym korytarzu - pokazuje, przy którym elemencie właśnie stoisz. Niektóre elementy HTML są naturalnie fokusowalne, czyli można na nie przejść Tabem bez żadnego atrybutu:
1<!-- Te elementy są naturalnie fokusowalne -->
2<a href="/link">Link</a>
3<button>Przycisk</button>
4<input type="text">
5<select>...</select>
6<textarea>...</textarea>
7
8<!-- Te elementy NIE są naturalnie fokusowalne -->
9<div>Nie można przejść Tabem</div>
10<span>To też nie</span>
11<p>I to nie</p>Linki z atrybutem href i przyciski od razu reagują też na klawisze: link na Enter, a przycisk na Enter i Spację. Zwykły tekst w <div>, <span> czy <p> nie jest interaktywny, więc Tab go pomija - i tak ma być, bo nikt nie chce zatrzymywać się na każdym akapicie.
Atrybut tabindex
Atrybut tabindex kontroluje, czy i kiedy element otrzymuje focus. Ma trzy rodzaje wartości - dwie przydatne i jedną, której należy unikać:
1<!-- tabindex="0" - element staje się fokusowalny w naturalnej kolejności -->
2<div tabindex="0" role="button" onclick="handleClick()">
3 Kliknij mnie
4</div>
5
6<!-- tabindex="-1" - fokusowalny programowo, ale NIE przez Tab -->
7<div tabindex="-1" id="error-message">
8 Wystąpił błąd!
9</div>
10
11<!-- tabindex="1+" - UNIKAJ! Zmienia naturalną kolejność -->
12<!-- To jest antypattern i powoduje chaos w nawigacji -->
13<input tabindex="3" placeholder="Trzeci">
14<input tabindex="1" placeholder="Pierwszy">
15<input tabindex="2" placeholder="Drugi">tabindex="0" wpisuje element do kolejności zgodnie z jego miejscem w kodzie. tabindex="-1" nie dodaje go do kolejności Tab, ale pozwala przenieść na niego focus skryptem, np. do komunikatu o błędzie. Wartości dodatnie wypychają elementy przed resztę strony, więc kolejność przestaje odpowiadać temu, co widać.
Zasada: Używaj tylko tabindex="0" i tabindex="-1". Nigdy nie używaj wartości dodatnich!
Zauważ też, że <div> z role="button" dostaje focus, ale nadal nie reaguje na Enter ani Spację - obsługę klawiszy trzeba dopisać w JavaScripcie. Dlatego polecam natywny <button>, który robi to za darmo.
Skip Links
Wyobraź sobie pielgrzyma, który przy każdym wejściu do świątyni musi minąć te same kolumny, zanim dojdzie do sanktuarium. Skip links pozwalają użytkownikom klawiatury przeskoczyć powtarzającą się nawigację i przejść bezpośrednio do głównej treści:
1<!-- Skip link - pierwszy element na stronie -->
2<a href="#main-content" class="skip-link">
3 Przejdź do treści głównej
4</a>
5
6<header>
7 <nav>
8 <!-- Długa nawigacja, którą użytkownik może pominąć -->
9 <a href="/">Strona główna</a>
10 <a href="/exhibitions">Wystawy</a>
11 <a href="/tickets">Bilety</a>
12 <a href="/contact">Kontakt</a>
13 </nav>
14</header>
15
16<main id="main-content">
17 <h1>Witaj w Muzeum Egipskim</h1>
18 <!-- ... -->
19</main>Link działa dzięki kotwicy: href="#main-content" wskazuje element z id="main-content". Sama nawigacja się nie zmienia - skip link tylko pozwala ją ominąć. Teraz trzeba go schować przed osobami z myszką i pokazać, gdy dostanie focus:
1/* Skip link ukryty wizualnie, ale widoczny na focus */
2.skip-link {
3 position: absolute;
4 top: -40px;
5 left: 0;
6 background: #000;
7 color: #fff;
8 padding: 8px 16px;
9 z-index: 100;
10 transition: top 0.3s;
11}
12
13.skip-link:focus {
14 top: 0;
15}Link czeka nad krawędzią strony, a :focus przywraca go na miejsce po pierwszym naciśnięciu Tab. Dokładna wartość (-40px czy -50px) nie ma znaczenia, byle link całkiem się chował. Nie używaj display: none, bo takiego elementu nie da się sfokusować.
Widoczny focus
Obwódka (outline) to jedyny sygnał, gdzie płonie pochodnia. Nigdy nie usuwaj outline z elementów fokusowalnych - porównaj dwa podejścia:
1/* ŹLE - ukrywanie focusu */
2*:focus {
3 outline: none; /* NIE RÓB TEGO! */
4}
5
6/* DOBRZE - customowy, ale widoczny focus */
7a:focus-visible,
8button:focus-visible {
9 outline: 3px solid #d4af37;
10 outline-offset: 2px;
11 border-radius: 2px;
12}Pseudoklasa :focus-visible jest lepsza niż :focus, bo pokazuje obwódkę przy nawigacji klawiaturą, a nie przy kliknięciu przycisku myszką (pola tekstowe pokazują ją zawsze, bo trzeba wiedzieć, gdzie się pisze). Dobra wersja niczego nie ukrywa - zmienia tylko kolor obwódki i odsuwa ją o 2px.
Focus trap w oknach dialogowych
Gdy otwierasz modal, focus musi być "uwięziony" w jego wnętrzu - inaczej użytkownik klawiatury będzie błądził po stronie ukrytej pod oknem. HTML ma do tego gotowy element:
1<dialog id="ticket-dialog" aria-labelledby="dialog-title">
2 <h2 id="dialog-title">Kup bilet</h2>
3 <form>
4 <label for="quantity">Liczba biletów:</label>
5 <input type="number" id="quantity" min="1" max="10" value="1">
6 <button type="submit">Kup</button>
7 <button type="button" onclick="closeDialog()">Anuluj</button>
8 </form>
9</dialog>Element <dialog> otwarty metodą showModal() automatycznie zarządza focusem: przenosi go do okna, a resztę strony czyni nieaktywną, więc Tab nie ucieknie za okno. Escape zamyka taki dialog, a focus wraca do elementu, który był aktywny przed otwarciem.
Dostępna nawigacja
Na koniec nawigacja z dodatkowymi rolami ARIA. Przyjrzyj się atrybutom role i aria-current:
1<nav aria-label="Nawigacja główna">
2 <ul role="menubar">
3 <li role="none">
4 <a href="/" role="menuitem">Strona główna</a>
5 </li>
6 <li role="none">
7 <a href="/exhibitions" role="menuitem"
8 aria-current="page">Wystawy</a>
9 </li>
10 <li role="none">
11 <a href="/tickets" role="menuitem">Bilety</a>
12 </li>
13 </ul>
14</nav>Atrybut aria-current="page" informuje czytnik ekranowy, na której stronie aktualnie jest użytkownik. Uwaga: role menubar i menuitem zapowiadają menu aplikacji obsługiwane strzałkami, które musisz sam oprogramować. Dla zwykłej nawigacji strony wystarczy <nav> z listą linków - i to polecam.
Nawigacja klawiaturowa to jak jasno wytyczone korytarze w piramidzie - bez nich nawet najlepiej zdobiona komnata pozostanie niedostępna dla wielu odwiedzających. W następnej lekcji wykorzystasz tę wiedzę w formularzach, gdzie Tab prowadzi od pola do pola.
Praktyka
Odłóż myszkę i przejdź stronę w edytorze poniżej samym Tabem. Sprawdź, czy skip link pojawia się jako pierwszy i czy zawsze widzisz, gdzie jest focus:
Kod do tej lekcji: index.html
1<!DOCTYPE html>
2<html lang="pl">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Nawigacja klawiaturowa</title>
7 <link rel="stylesheet" href="style.css">
8</head>
9<body>
10 <!-- Skip link -->
11 <a href="#main-content" class="skip-link">
12 Przejdz do tresci glownej
13 </a>
14
15 <header>
16 <h1>Muzeum Egipskie</h1>
17 <nav aria-label="Nawigacja glowna">
18 <ul>
19 <li><a href="#exhibitions" aria-current="page">Wystawy</a></li>
20 <li><a href="#tickets">Bilety</a></li>
21 <li><a href="#contact">Kontakt</a></li>
22 </ul>
23 </nav>
24 </header>
25
26 <main id="main-content">
27 <h2>Testuj nawigacje klawiaturowa</h2>
28 <p>Uzyj klawisza <kbd>Tab</kbd> aby przechodzic miedzy elementami.
29 Sprobuj <kbd>Shift+Tab</kbd> aby wrocic.</p>
30
31 <section id="exhibitions">
32 <h3>Interaktywne elementy</h3>
33
34 <a href="#link1">Link do wystawy 1</a>
35 <a href="#link2">Link do wystawy 2</a>
36
37 <button onclick="alert('Kliknieto!')">
38 Kliknij mnie (Enter lub Spacja)
39 </button>
40
41 <div class="custom-btn" tabindex="0" role="button"
42 onclick="alert('Div-button!')"
43 onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();alert('Div-button!')}">
44 Div z tabindex="0" (tez fokusowalny)
45 </div>
46 </section>
47
48 <section id="tickets">
49 <h3>Formularz z nawigacja klawiaturowa</h3>
50 <form>
51 <label for="fname">Imie:</label>
52 <input type="text" id="fname" name="fname">
53
54 <label for="lname">Nazwisko:</label>
55 <input type="text" id="lname" name="lname">
56
57 <label for="ticket-type">Typ biletu:</label>
58 <select id="ticket-type" name="ticket">
59 <option value="normal">Normalny</option>
60 <option value="reduced">Ulgowy</option>
61 <option value="family">Rodzinny</option>
62 </select>
63
64 <button type="submit">Kup bilet</button>
65 </form>
66 </section>
67 </main>
68
69 <footer>
70 <p>Sprobuj dotrzec tutaj Tabem - ile razy musisz go nacisnac?</p>
71 <p>A teraz wroc na gore i uzyj skip linka!</p>
72 </footer>
73</body>
74</html>Pamiętaj: Tab to pielgrzym idący korytarzami Twojej piramidy - skip link skraca mu drogę, a widoczny focus jest pochodnią, która pokazuje, gdzie stoi.
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Które wartości atrybutu tabindex są zalecane do używania?
Zadania praktyczne w grze
- Klikanie w kolejności
Ułóż poprawny skip link w HTML:
- Edytor kodu
Dodaj skip link na początku strony, id="main-content" do main, i style CSS dla .skip-link (ukryty, widoczny na focus). Dodaj też focus-visible dla a, button, input.
- Układanie w pionie
Ułóż typową kolejność nawigacji Tab na stronie internetowej: