WCAG 2.1.3: Klawiatura (bez wyjątku)
WCAG 2.1.3 Klawiatura (bez wyjątku) – Poziom AAA
Kryterium sukcesu 2.1.3, zatytułowane „Klawiatura (bez wyjątku)”, jest kluczowym elementem zapewnienia dostępności stron internetowych na poziomie AAA. Wymaga ono, aby wszystkie funkcje dostępne w treści były obsługiwane za pomocą klawiatury, bez żadnych wyjątków. Oznacza to, że każdy element interaktywny, każda operacja i każda funkcja dostępna dla użytkownika za pomocą myszy lub ekranu dotykowego, musi być również w pełni dostępna i obsługiwalna wyłącznie za pomocą klawiatury.
Podczas gdy kryterium 2.1.1 Klawiatura (Poziom A) dopuszcza pewne wyjątki dla funkcji, które wymagają wejścia zależnego od ścieżki ruchu użytkownika (np. swobodne rysowanie), kryterium 2.1.3 idzie o krok dalej. Na poziomie AAA, jeśli funkcja zasadniczo wymaga wejścia zależnego od ścieżki, ale istnieją alternatywne metody interakcji (które nie zależą od ścieżki) dla osiągnięcia tej samej funkcjonalności, wówczas te alternatywy muszą być dostępne za pomocą klawiatury.
Dlaczego to ma znaczenie?
Dostępność klawiatury jest fundamentalna dla szerokiego grona użytkowników:
- Użytkownicy z niepełnosprawnością ruchową: Osoby, które nie mogą używać myszy z powodu drżenia rąk, paraliżu lub innych ograniczeń motorycznych, polegają wyłącznie na klawiaturze, przełącznikach lub innych urządzeniach emulujących klawiaturę.
- Użytkownicy niewidomi lub słabowidzący: Osoby te często korzystają z czytników ekranu, które w dużej mierze opierają się na nawigacji klawiaturą, aby poruszać się po treści i interaktywnych elementach. Brak dostępu klawiaturą uniemożliwia im korzystanie ze strony.
- Użytkownicy z niepełnosprawnością poznawczą: Niektóre osoby mogą mieć trudności z precyzyjnym klikaniem myszą lub rozumieniem złożonych interakcji wymagających koordynacji ręka-oko. Klawiatura oferuje prostszą, sekwencyjną metodę interakcji.
- Użytkownicy urządzeń mobilnych: Chociaż ekrany dotykowe są powszechne, wiele osób używa zewnętrznych klawiatur do precyzyjnego wprowadzania danych lub szybszej nawigacji.
Jeśli jakakolwiek funkcja nie jest dostępna za pomocą klawiatury, wymienione grupy użytkowników zostają całkowicie wykluczone z możliwości jej użycia, co prowadzi do frustracji i niemożności realizacji zadań na stronie.
Wymagania Kryterium Sukcesu 2.1.3
Oficjalne sformułowanie kryterium sukcesu 2.1.3 brzmi:
Cała funkcjonalność treści jest obsługiwana za pomocą interfejsu klawiatury, bez konieczności określonych czasów dla poszczególnych naciśnięć klawiszy, z wyjątkiem przypadków, gdy podstawowa funkcja wymaga wprowadzania danych, które zależy od ścieżki ruchu użytkownika, a nie tylko od punktów końcowych.
Uwaga: To kryterium jest na poziomie AAA i jest bardziej restrykcyjne niż 2.1.1. Jeśli podstawowa funkcja wymaga wprowadzania danych, które zależy od ścieżki ruchu, a istnieje alternatywna metoda interakcji, która nie zależy od ścieżki ruchu, to ta alternatywna metoda musi być dostępna za pomocą klawiatury.
Kluczowe aspekty to:
- Cała funkcjonalność: Nie ma wyjątków dla poszczególnych elementów czy sekcji. Wszystko, co jest interaktywne, musi być obsługiwane klawiaturą.
- Interfejs klawiatury: Oznacza to używanie klawiszy takich jak Tab, Shift+Tab, Enter, Spacja, klawisze strzałek oraz inne standardowe skróty klawiaturowe.
- Brak wymogów czasowych: Użytkownicy nie powinni być zmuszeni do wykonywania szybkich lub precyzyjnych sekwencji klawiszy, aby uruchomić funkcję.
- Minimalne wyjątki (poziom AAA): Jeśli funkcja z natury wymaga wejścia zależnego od ścieżki (np. swobodne rysowanie, przeciąganie i upuszczanie), ale można zaoferować alternatywną, niezależną od ścieżki metodę wykonania tej samej operacji (np. wybór punktów początkowych/końcowych dla rysowania, menu kontekstowe dla przenoszenia elementów), to ta alternatywa musi być dostępna klawiaturą. Jeśli nie ma żadnej sensownej alternatywy niezależnej od ścieżki, to tylko wtedy może to być wyjątek. W praktyce bardzo niewiele funkcji internetowych kwalifikuje się do tego wyjątku na poziomie AAA.
Praktyczne wytyczne dla zgodności
- Używaj semantycznych elementów HTML: Przyciski (`<button>`), linki (`<a>`), pola formularzy (`<input>`, `<textarea>`, `<select>`) są domyślnie dostępne klawiaturą. Zawsze preferuj je nad niestandardowymi elementami.
- Zapewnij fokus klawiatury: Wszystkie interaktywne elementy muszą być osiągalne za pomocą klawisza Tab (lub Shift+Tab dla cofania). Muszą również posiadać widoczny wskaźnik fokusu (np. obramowanie, zmiana tła), aby użytkownik wiedział, gdzie aktualnie znajduje się jego fokus.
- Obsługa zdarzeń klawiatury dla niestandardowych elementów: Jeśli musisz tworzyć niestandardowe widżety (np. niestandardowe przełączniki, rozwijane menu, suwaki), upewnij się, że:
- Mają `tabindex=”0″`, aby były focusowalne.
- Obsługują odpowiednie zdarzenia klawiatury (np. `keydown`, `keyup`), aby reagować na klawisz Enter lub Spacja (dla aktywacji), klawisze strzałek (dla nawigacji w obrębie komponentu, np. w menu, listach, karuzelach), Esc (dla zamknięcia okien modalnych).
- Ich stan jest poprawnie komunikowany za pomocą atrybutów ARIA (np. `aria-expanded`, `aria-selected`, `aria-checked`).
- Unikaj pułapek klawiaturowych: Upewnij się, że fokus klawiatury nigdy nie utknie w żadnym elemencie lub sekcji strony. Zawsze musi być możliwość wyjścia z aktywnego elementu i kontynuowania nawigacji po reszcie treści.
- Alternatywy dla interakcji zależnych od ścieżki: Jeśli masz elementy takie jak „przeciągnij i upuść” (drag and drop), zapewnij alternatywne metody ich obsługi klawiaturą. Może to być np. menu kontekstowe z opcjami „przenieś do”, przyciski do zmiany kolejności elementów (góra/dół), lub specjalny tryb klawiaturowy.
- Zarządzanie fokusem w dynamicznych treściach: Kiedy treści pojawiają się lub znikają (np. modale, powiadomienia, okna dialogowe), upewnij się, że fokus klawiatury jest odpowiednio przenoszony. Po otwarciu modalu fokus powinien przenieść się do niego, a po zamknięciu – wrócić do elementu, który otworzył modal.
Przykłady implementacji
Przykład 1: Niestandardowy przycisk (zły vs. dobry)
Nieprawidłowa implementacja: Przycisk obsługiwany tylko myszą.
<div onclick="doSomething()" style="padding: 10px; background: lightblue; cursor: pointer;">Kliknij mnie</div>
Problem: Ten `div` nie jest focusowalny klawiaturą i nie reaguje na klawisze Enter/Spacja.
Prawidłowa implementacja: Niestandardowy przycisk dostępny klawiaturą.
<button onclick="doSomething()" style="padding: 10px; background: lightgreen; border: 1px solid darkgreen; cursor: pointer;">Kliknij mnie</button>
Lub dla niestandardowego elementu bez semantyki buttona:
<div tabindex="0" role="button" aria-label="Kliknij, aby wykonać akcję"
onclick="doSomething()"
onkeydown="if (event.key === 'Enter' || event.key === ' ') { doSomething(); }"
style="padding: 10px; background: lightgreen; cursor: pointer; outline: 2px solid blue; outline-offset: 2px;">
Kliknij mnie (niestandardowy)
</div>
Rozwiązanie: Użycie elementu `<button>` lub, w przypadku `<div>`, dodanie `tabindex=”0″`, `role=”button”` oraz obsługi zdarzeń `keydown` dla klawiszy Enter i Spacja. Dodano również widoczny `outline` dla fokusu.
Przykład 2: Okno modalne (zarządzanie fokusem)
Dla okien modalnych kluczowe jest zapewnienie, że fokus jest przenoszony do okna po jego otwarciu i z powrotem do elementu wywołującego po jego zamknięciu. Ponadto, wszystkie interaktywne elementy w oknie modalnym muszą być dostępne klawiaturą, a fokus musi być „uwięziony” w oknie modalnym (tzw. focus trap) dopóki nie zostanie ono zamknięte.
Prawidłowa implementacja: Dostępne klawiaturowo okno modalne.
<!-- Przycisk otwierający modal -->
<button id="openModalBtn">Otwórz Modal</button>
<!-- Okno modalne -->
<div id="myModal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" style="display: none;">
<h3 id="modalTitle">Tytuł Modalu</h3>
<p>Zawartość okna modalnego.</p>
<input type="text" placeholder="Wprowadź tekst">
<button id="closeModalBtn">Zamknij</button>
</div>
<script>
const openBtn = document.getElementById('openModalBtn');
const modal = document.getElementById('myModal');
const closeBtn = document.getElementById('closeModalBtn');
const firstFocusableElement = modal.querySelector('input'); // Pierwszy element focusowalny w modalu
let lastFocusedElement;
function openModal() {
lastFocusedElement = document.activeElement; // Zapamiętaj element, który miał fokus przed otwarciem modalu
modal.style.display = 'block';
firstFocusableElement.focus(); // Przenieś fokus do pierwszego elementu w modalu
document.addEventListener('keydown', handleModalKeydown);
}
function closeModal() {
modal.style.display = 'none';
lastFocusedElement.focus(); // Przywróć fokus do elementu, który otworzył modal
document.removeEventListener('keydown', handleModalKeydown);
}
function handleModalKeydown(event) {
if (event.key === 'Escape') {
closeModal();
return;
}
// Prosta logika pułapki fokusu (wymaga rozbudowania dla złożonych modali)
if (event.key === 'Tab') {
const focusableElements = modal.querySelectorAll('a[href], button, input, textarea, select, [tabindex="0"]');
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
if (event.shiftKey) { // Shift + Tab
if (document.activeElement === first) {
last.focus();
event.preventDefault();
}
} else { // Tab
if (document.activeElement === last) {
first.focus();
event.preventDefault();
}
}
}
}
openBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
</script>
Rozwiązanie: Zapewniono obsługę otwierania/zamykania modalu, przenoszenia fokusu, zamykania klawiszem Esc oraz podstawową pułapkę fokusu, aby użytkownik klawiatury nie mógł wyjść z modalu, dopóki nie zostanie on zamknięty.
Przykład 3: Alternatywa dla przeciągnij i upuść (drag and drop)
Prawidłowa implementacja: Przenoszenie elementów za pomocą klawiatury.
<ul id="draggableList">
<li tabindex="0" role="listitem" draggable="true" aria-label="Element 1, możesz go przenieść">Element 1</li>
<li tabindex="0" role="listitem" draggable="true" aria-label="Element 2, możesz go przenieść">Element 2</li>
<li tabindex="0" role="listitem" draggable="true" aria-label="Element 3, możesz go przenieść">Element 3</li>
</ul>
<button id="moveUpBtn">Przenieś w górę</button>
<button id="moveDownBtn">Przenieś w dół</button>
<script>
const list = document.getElementById('draggableList');
const moveUpBtn = document.getElementById('moveUpBtn');
const moveDownBtn = document.getElementById('moveDownBtn');
let currentFocusedItem = null;
list.addEventListener('focusin', (event) => {
currentFocusedItem = event.target.closest('li');
});
moveUpBtn.addEventListener('click', () => {
if (currentFocusedItem && currentFocusedItem.previousElementSibling) {
list.insertBefore(currentFocusedItem, currentFocusedItem.previousElementSibling);
currentFocusedItem.focus(); // Utrzymaj fokus na przeniesionym elemencie
}
});
moveDownBtn.addEventListener('click', () => {
if (currentFocusedItem && currentFocusedItem.nextElementSibling) {
list.insertBefore(currentFocusedItem.nextElementSibling, currentFocusedItem);
currentFocusedItem.focus(); // Utrzymaj fokus na przeniesionym elemencie
}
});
// Upewnij się, że elementy listy są focusowalne i obsługują Enter dla ewentualnych akcji kontekstowych
list.querySelectorAll('li').forEach(item => {
item.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
// Tutaj można zaimplementować np. otwarcie menu kontekstowego dla elementu
console.log('Akcja dla elementu: ' + item.textContent);
}
});
});
</script>
Rozwiązanie: Zamiast polegać wyłącznie na przeciąganiu i upuszczaniu myszą, zapewniono przyciski „Przenieś w górę” i „Przenieś w dół”, które pozwalają użytkownikom klawiatury zmieniać kolejność elementów listy. Elementy listy są również focusowalne (`tabindex=”0″`) i posiadają atrybuty ARIA.
Najlepsze praktyki i typowe pułapki
Najlepsze praktyki:
- Testuj regularnie za pomocą klawiatury: Po prostu odłóż mysz i spróbuj przejść przez całą stronę, używając tylko klawisza Tab, Shift+Tab, Enter, Spacja i klawiszy strzałek. To najlepszy sposób na wykrycie problemów.
- Wizualny wskaźnik fokusu: Zawsze upewnij się, że użytkownik wie, który element jest aktualnie aktywny. Domyślne wskaźniki przeglądarki są często niewystarczające. Dostosuj je za pomocą CSS (`:focus { outline: 2px solid blue; }`).
- Sekwencyjna nawigacja: Upewnij się, że kolejność fokusu klawiatury jest logiczna i zgodna z wizualnym układem strony. Użyj `tabindex` tylko wtedy, gdy jest to absolutnie konieczne do zmiany naturalnej kolejności (np. `tabindex=”-1″` dla elementów, które powinny być fokusowalne programowo, ale nie poprzez Tab).
- Wsparcie dla skrótów klawiaturowych: Rozważ dodanie globalnych skrótów klawiaturowych dla często używanych funkcji, ale upewnij się, że nie kolidują one ze skrótami przeglądarki lub systemowymi.
- Dokumentacja i pomoc: Jeśli strona ma złożone interakcje klawiaturowe, udostępnij użytkownikom sekcję pomocy lub dokumentacji, która je wyjaśnia.
Typowe pułapki:
- `div` lub `span` z `onclick`: Używanie nieinteraktywnych elementów jako przycisków bez odpowiednich atrybutów (`tabindex`, `role`) i obsługi zdarzeń klawiatury.
- Ukrywanie `outline` fokusu: Resetowanie `outline: none;` w CSS bez zapewnienia alternatywnego, widocznego wskaźnika fokusu.
- Blokowanie przewijania klawiszami strzałek: Niestandardowe widżety, które przechwytują zdarzenia klawiszy strzałek, uniemożliwiając użytkownikowi przewijanie strony.
- Brak obsługi Esc: Okna modalne, rozwijane menu lub inne tymczasowe interfejsy nie mogą być zamknięte za pomocą klawisza Esc.
- Niewystarczające alternatywy dla funkcji zależnych od ścieżki: Zapewnienie alternatywy, która nadal wymaga precyzyjnego ruchu (np. sterowanie kursorem za pomocą klawiszy strzałek dla rysowania, co może być trudne). Celem jest alternatywa, która nie wymaga tej precyzji.
Zgodność z WCAG 2.1.3 Klawiatura (bez wyjątku) wymaga skrupulatności i dokładnego testowania. Implementacja tego kryterium na poziomie AAA znacznie poprawia dostępność strony dla osób polegających na klawiaturze, zapewniając im pełną i równą możliwość interakcji z każdą funkcją oferowaną przez treści internetowe.