Klawiatura to podstawowe narzędzie dostępności — i źródło połowy problemów w audytach WCAG. Focus, tabindex, focus trap, skróty, semantyczny HTML — praktyczny przewodnik.
Klawiatura to podstawowe narzędzie pracy — i dostępności
Większość osób kojarzy obsługę klawiaturą z wygodą i skrótami klawiszowymi. W praktyce to jednak coś więcej — dla użytkowników, którzy nie mogą korzystać z myszy (osoby z niepełnosprawnością ruchową, użytkownicy czytników ekranu, osoby pracujące z urządzeniami bez ekranu dotykowego), klawiatura jest jedynym sposobem nawigacji po aplikacji.
Standard WCAG 2.2, kryterium 2.1.1, mówi wprost: cała funkcjonalność musi być dostępna z klawiatury. To nie sugestia — to warunek spełnienia poziomu A. W audytach dostępności, które realizujemy, problemy z obsługą klawiaturą pojawiają się w ponad połowie badanych aplikacji.
Focus, tabindex i kolejność nawigacji
Nawigacja klawiaturą opiera się na mechanizmie focusa — podświetleniu aktywnego elementu podczas przechodzenia klawiszem Tab. Żeby to działało poprawnie, trzeba rozumieć trzy wartości atrybutu tabindex:
tabindex="0"— element uczestniczy w naturalnej kolejności tabulacji (zgodnie z pozycją w DOM). Używamy go, gdy chcemy dołączyć do nawigacji element, który domyślnie nie jest interaktywny.tabindex="-1"— element jest pomijany w nawigacji Tab, ale można go sfocusować programowo (np.element.focus()). Przydatne przy modalnach i dynamicznych widokach.tabindex="1"i wyżej — wymusza pierwszeństwo w nawigacji, ale w praktyce jest antywzorcem, bo łamie naturalną kolejność i utrudnia orientację.
Samo ustawienie tabindex to za mało. Trzeba jeszcze zadbać o widoczność focusa. Przeglądarka daje domyślny outline, ale wiele projektów go ukrywa (outline: none) ze względów estetycznych. Lepsze podejście to nadpisanie stylu focusa za pomocą :focus-visible:
button:focus-visible,
a:focus-visible { outline: 2px solid #1a73e8; outline-offset: 2px;
}Pseudoklasa :focus-visible aktywuje się tylko przy nawigacji klawiaturą, nie przy kliknięciu myszą — dzięki temu nie irytuje użytkowników myszy, a jednocześnie wspiera tych, którzy potrzebują wskazówki wizualnej.
Semantyczny HTML kontra div z onclick
Elementy <a>, <button>, <input> i <select> są domyślnie focusowalne i reagują na klawiaturę — Enter aktywuje link i przycisk, spacja zaznacza checkbox. Problem pojawia się, gdy programiści używają <div> lub <span> z obsługą kliknięcia przez JavaScript:
<!-- Tak nie rób -->
<div onclick="zapisz()">Zapisz</div>
<!-- Użyj natywnego elementu -->
<button type="button" onclick="zapisz()">Zapisz</button>Różnica wydaje się kosmetyczna, ale <div> nie jest focusowalny, nie reaguje na Enter ani spację i nie jest rozpoznawany przez czytniki ekranu jako element interaktywny. Żeby go „naprawić", trzeba dodać role="button", tabindex="0" i obsługę zdarzeń keydown — trzy linie kodu, które natywny <button> daje za darmo.
W praktyce widzę ten problem regularnie w aplikacjach budowanych na komponentach frameworków (React, Angular, Vue), gdzie stylowany <div> jest prostszy do zaimplementowania niż poprawnie ostylowany <button>. To zawsze dług technologiczny, który wraca przy pierwszym teście dostępności.
Skróty klawiaturowe — wygoda, ale z rozwagą
Skróty klawiaturowe przyspieszają pracę w aplikacjach profesjonalnych. Gmail, Jira, Slack, Figma — każde z tych narzędzi ma własny zestaw kombinacji. Problem zaczyna się, gdy skróty są nieintuicyjne, nieudokumentowane lub kolidują ze skrótami systemowymi.
Kilka realnych pułapek:
- Gmail: klawisz E archiwizuje wiadomość. Jedno przypadkowe naciśnięcie — i mail znika z widoku. Użytkownik nie dostaje potwierdzenia.
- Asana: Tab+T otwiera nowe zadanie. Kombinacja łatwa do przypadkowego wywołania podczas nawigacji tabulatorem.
- Konflikty systemowe: Alt+F4 zamyka okno, Ctrl+W zamyka kartę — jeśli aplikacja przechwytuje te kombinacje, użytkownik traci kontrolę nad systemem.
Dobra praktyka to oferowanie widoku pomocy z listą skrótów (często ? lub Ctrl+/) i możliwości ich wyłączenia lub zmiany. WCAG 2.2, kryterium 2.1.4 (Character Key Shortcuts), wymaga, żeby skróty jednoznakowe dało się wyłączyć lub przemapować.
Modale, ESC i pułapka focusa
Okna modalne to jedno z najtrudniejszych miejsc w obsłudze klawiaturą. Poprawnie zaprojektowany modal powinien:
- Przenieść focus na pierwszy interaktywny element po otwarciu.
- Zamykać się po naciśnięciu Esc.
- Wykonywać główną akcję po Enter (jeśli to dialog potwierdzenia).
- Zapętlać focus w swoim obrębie — tzw. focus trap. Tab na ostatnim elemencie wraca do pierwszego, Shift+Tab na pierwszym — do ostatniego.
Bez focus trap użytkownik klawiatury może „uciec" z modala w tło strony, nie widząc ani treści modala, ani kontekstu strony. To jedna z najczęstszych barier, którą spotykamy podczas testów dostępności.
Nowoczesne przeglądarki wspierają natywny element <dialog> z wbudowanym zarządzaniem focusa i obsługą Esc. Jeśli piszesz modalne od zera w React czy Vue, warto sięgnąć po biblioteki takie jak HeadlessUI lub Radix, które implementują ARIA poprawnie.
Backspace, historia nawigacji i formularze
Klawisz Backspace w przeglądarkach przez lata cofał użytkownika na poprzednią stronę. To było źródło frustracji — ktoś wypełniał formularz, przypadkiem kliknął Backspace poza polem tekstowym i tracił wszystkie dane. Chrome od wersji 52 (2016) wyłączył to zachowanie. Firefox nadal je wspiera w domyślnej konfiguracji.
Niezależnie od przeglądarki, dobrą praktyką w aplikacjach z formularzami jest:
- Przechwytywanie zdarzenia
beforeunload, żeby ostrzec użytkownika przed utratą danych. - Zapisywanie stanu formularza w
sessionStoragelub stanie aplikacji, żeby dało się go odzyskać. - Unikanie przechwytywania Backspace globalnie — użytkownik oczekuje, że ten klawisz kasuje znaki w polu tekstowym.
Powiadomienia — toast to nie potwierdzenie
Wiele aplikacji zastąpiło klasyczne dialogi potwierdzenia krótkimi toastami (dymkami informacyjnymi, które znikają po kilku sekundach). To wygodne dla szybkich operacji, ale niebezpieczne przy akcjach nieodwracalnych.
Toast, który znika po 3 sekundach, może nie zostać przeczytany przez osobę korzystającą z czytnika ekranu — chyba że ma atrybut role="status" lub aria-live="polite". Trwałe akcje (usunięcie konta, wysłanie wiadomości, zatwierdzenie płatności) powinny wymagać jawnego potwierdzenia z focusem na przyciskach Tak/Nie.
Checklist — co sprawdzić w swojej aplikacji
- Czy da się przejść przez całą aplikację samym Tabem? Sprawdź formularz, menu, modal, nawigację.
- Czy focus jest zawsze widoczny? Wyłącz mysz i przejdź przez stronę — jeśli nie wiesz, gdzie jesteś, użytkownik klawiatury też nie wie.
- Czy modalne mają focus trap i zamykają się po Esc?
- Czy skróty klawiaturowe są udokumentowane i da się je wyłączyć?
- Czy elementy interaktywne to natywne
<button>i<a>, a nie<div>z onclick? - Czy toasty z informacją o błędzie/sukcesie mają
aria-live?
Obsługa klawiaturą to jeden z fundamentów tworzenia aplikacji webowych. Nie wymaga dużych nakładów, ale wymaga świadomości. Jeśli chcesz sprawdzić, jak Twoja aplikacja radzi sobie z nawigacją klawiaturą — skontaktuj się z nami.
Źródła
- W3C WCAG 2.2 — Keyboard Accessible — wymagania dotyczące dostępności klawiaturowej (kryteria 2.1.1–2.1.4).
- MDN Web Docs — Keyboard-navigable JavaScript widgets — przewodnik po implementacji nawigacji klawiaturowej w widgetach JS.
- WebAIM — Keyboard Accessibility — techniki testowania i wdrażania obsługi klawiaturą.
- MDN Web Docs — :focus-visible — dokumentacja pseudoklasy do stylowania focusa klawiaturowego.
- W3C ARIA Authoring Practices — Dialog (Modal) — wzorzec implementacji modalnych okien dialogowych z poprawnym zarządzaniem focusem.