Najczęstsze błędy WCAG od lat są te same, a większość z nich naprawia się prostymi zmianami w kodzie. Raport WebAIM Million 2025, w którym przeanalizowano strony główne miliona popularnych witryn, wykrył automatycznie błędy WCAG 2 na ok. 95% z nich.
Poniżej omawiamy typy błędów z czołówki tego zestawienia – z przykładami kodu „źle/dobrze” i powiązanymi kryteriami – a na końcu kilka problemów spoza listy, które regularnie wychodzą w audytach, choć automat zwykle ich nie wychwyci.
Najczęstsze błędy WCAG według WebAIM Million
Zestawienie WebAIM Million powstaje co roku, a kolejność najczęstszych typów błędów od lat się nie zmienia:
| Błąd | Kryterium WCAG | Poziom |
|---|---|---|
| 1. Niski kontrast tekstu | 1.4.3 Contrast (Minimum) | AA |
| 2. Brak tekstu alternatywnego obrazów | 1.1.1 Non-text Content | A |
| 3. Pola formularzy bez etykiet | 1.3.1, 3.3.2, 4.1.2 | A |
| 4. Puste linki | 2.4.4 Link Purpose (In Context), 4.1.2 | A |
| 5. Puste przyciski | 4.1.2 Name, Role, Value | A |
| 6. Brak określenia języka dokumentu | 3.1.1 Language of Page | A |
To wyłącznie błędy wykrywalne automatycznie, więc realny obraz jest gorszy. Według Deque (2021) testy axe-core wykryły ok. 57% problemów liczonych według liczby wystąpień, a inne badania podają inne wartości. Różnice między metodami opisujemy w artykule test automatyczny a audyt manualny.
Niski kontrast tekstu
- Dlaczego to problem
- Jasnoszary tekst na białym tle jest trudny do odczytania dla osób słabowidzących, starszych i dla każdego, kto patrzy na ekran w słońcu.
- Jak wykryć
- Test automatyczny znajdzie większość przypadków na jednolitym tle. Tekst na zdjęciach i gradientach sprawdź ręcznie.
- Kryterium
- 1.4.3 Contrast (Minimum) – kontrast minimalny: 4,5:1 dla zwykłego tekstu, 3:1 dla dużego.
/* Źle: 2,85:1 */
.opis { color: #999999; background: #ffffff; }
/* Dobrze: 4,54:1 */
.opis { color: #767676; background: #ffffff; }
Progi, wzór i typowe pułapki omawiamy w artykule o kontraście kolorów WCAG.
Brak tekstu alternatywnego obrazów
- Dlaczego to problem
- Użytkownik czytnika ekranu nie wie, co przedstawia obraz, a przy obrazie w linku – dokąd link prowadzi. Czytnik może zamiast tego odczytać nazwę pliku.
- Jak wykryć
- Brak atrybutu alt wykryje automat. To, czy opis ma sens, oceni tylko człowiek.
- Kryterium
- 1.1.1 Non-text Content – treść nietekstowa.
<!-- Źle -->
<img src="DSC_0192.jpg">
<!-- Dobrze: obraz informacyjny i dekoracyjny -->
<img src="DSC_0192.jpg" alt="Serwisant wymienia filtr w pompie ciepła">
<img src="fala.svg" alt="">
Jak pisać opisy w różnych sytuacjach, wyjaśniamy w poradniku o tekście alternatywnym.
Pola formularzy bez etykiet
- Dlaczego to problem
- Czytnik ekranu poinformuje tylko, że to pole tekstowe, więc użytkownik nie wie, co wpisać. Placeholder nie zastępuje etykiety: znika przy pisaniu i bywa zbyt jasny.
- Jak wykryć
- Pola bez nazwy wykryje automat. Czy etykieta jest zrozumiała, sprawdzisz ręcznie.
- Kryteria
- 1.3.1 Info and Relationships – informacje i relacje, 3.3.2 Labels or Instructions – etykiety lub instrukcje, 4.1.2 Name, Role, Value – nazwa, rola, wartość.
<!-- Źle: tekst obok pola nie jest z nim powiązany -->
<span>Kod pocztowy</span>
<input type="text" name="kod">
<!-- Dobrze -->
<label for="kod">Kod pocztowy</label>
<input type="text" id="kod" name="kod" autocomplete="postal-code">
Jeśli etykieta ma być niewidoczna, np. w polu wyszukiwarki obok przycisku z lupą, zostaw element <label> ukryty wizualnie klasą visually-hidden albo nadaj polu atrybut aria-label.
Puste linki i puste przyciski
- Dlaczego to problem
- Link lub przycisk bez tekstu czytnik ogłosi jako sam „link” czy „przycisk” albo odczyta adres URL, a osoba sterująca głosem nie ma jak go nazwać. Zwykle chodzi o ikony: koszyk, wyszukiwarkę, menu, strzałki karuzeli, media społecznościowe.
- Jak wykryć
- Brak nazwy automat wykrywa dobrze. Ręcznie sprawdź, czy nazwa jest zrozumiała.
- Kryteria
- 2.4.4 Link Purpose (In Context) – cel linku (w kontekście) oraz 4.1.2 Name, Role, Value.
<!-- Źle -->
<button type="button" class="menu-toggle"><svg>…</svg></button>
<a href="/koszyk"><i class="icon-cart"></i></a>
<!-- Dobrze -->
<button type="button" class="menu-toggle" aria-expanded="false">
<svg aria-hidden="true">…</svg> Menu
</button>
<a href="/koszyk">
<i class="icon-cart" aria-hidden="true"></i>
<span class="visually-hidden">Koszyk</span>
</a>
Nazwę można też nadać atrybutem aria-label, ale widoczny podpis pomaga wszystkim użytkownikom, nie tylko tym, którzy korzystają z czytników ekranu.
Brak określenia języka strony
- Dlaczego to problem
- Czytnik ekranu dobiera wymowę do języka dokumentu. Bez atrybutu
langpolski tekst może zostać przeczytany z angielską wymową i stać się niezrozumiały. Język wpływa też na automatyczne tłumaczenie i dzielenie wyrazów. - Jak wykryć
- Automatycznie i bez trudu – to jeden z testów bezpłatnego skanu wstępnego na stronie głównej. Ręcznie sprawdź, czy język jest właściwy: motyw ustawiający
lang="en-US"na polskiej stronie to także błąd. - Kryterium
- 3.1.1 Language of Page – język strony. Fragmenty w innym języku obejmuje 3.1.2 Language of Parts – język części (AA).
<!-- Źle -->
<html>
<!-- Dobrze -->
<html lang="pl">
…
<p>Hasło ruchu: <span lang="en">Nothing about us without us</span></p>
W WordPressie język strony ustawisz w menu Ustawienia → Ogólne → Język witryny. Motyw powinien przekazać go do znacznika <html> – sprawdź to w kodzie strony.
Częste błędy, których automat zwykle nie wychwyci
Te problemy łatwo przeoczyć, bo ujawniają się dopiero podczas obsługi strony klawiaturą lub czytnikiem ekranu, a nie w samej analizie kodu.
Niewidoczny fokus
Reguła outline: none bez zamiennika sprawia, że osoba korzystająca z klawiatury nie wie, gdzie jest. To naruszenie 2.4.7 Focus Visible – widoczny fokus (AA). Wykryjesz je, przechodząc stronę klawiszem Tab.
/* Źle */
:focus { outline: none; }
/* Dobrze: wyraźny obrys z kontrastem co najmniej 3:1 względem tła */
:focus-visible { outline: 3px solid #1a56db; outline-offset: 2px; }
Pułapka klawiaturowa w oknie modalnym
Skrypt zatrzymuje fokus w oknie, ale przycisk zamknięcia to <div> bez obsługi klawiatury, a Esc nie działa – użytkownik utknął. To naruszenie 2.1.2 No Keyboard Trap – brak pułapki klawiaturowej (A). Odwrotny błąd to fokus uciekający pod otwarte okno, do zasłoniętej treści (2.4.3 Focus Order – kolejność fokusu). Dużą część pracy wykona natywny element <dialog> otwierany metodą showModal(): blokuje resztę strony i zamyka się klawiszem Esc.
<dialog id="dostawa" aria-labelledby="dostawa-tytul">
<h2 id="dostawa-tytul">Koszty dostawy</h2>
…
<form method="dialog"><button>Zamknij</button></form>
</dialog>
Po zamknięciu okna fokus powinien wrócić do przycisku, który je otworzył – sprawdź to klawiaturą.
Komunikaty błędów w formularzach
Pole obrysowane na czerwono bez żadnego tekstu to za mało: osoba niewidoma lub nierozróżniająca kolorów nie wie, co poszło nie tak. Kryterium 3.3.1 Error Identification – identyfikacja błędów (A) wymaga opisania błędu tekstem, a 1.4.1 Use of Color – użycie koloru (A) nie pozwala przekazywać informacji wyłącznie kolorem. Powiąż komunikat z polem:
<label for="kod">Kod pocztowy</label>
<input id="kod" name="kod" aria-invalid="true" aria-describedby="kod-blad">
<p id="kod-blad">Wpisz kod w formacie 00-000.</p>
Podpowiedz też, jak poprawić błąd – tego wymaga 3.3.3 Error Suggestion – sugestia poprawy (AA). Po wysłaniu formularza przenieś fokus do pierwszego błędnego pola albo do podsumowania błędów na początku formularza.
Nagłówki udające tekst i tekst udający nagłówki
Czytniki ekranu pozwalają przeskakiwać po nagłówkach jak po spisie treści. Śródtytuł zrobiony z pogrubionego akapitu w tym spisie się nie pojawi. Z kolei <h3> użyty tylko po to, żeby napis był większy, zaśmieca strukturę. Oba przypadki dotyczą 1.3.1 Info and Relationships. Wygląd ustawiaj w CSS, a poziom nagłówka dobieraj do struktury treści.
<!-- Źle -->
<p class="duzy-pogrubiony">Dane do wysyłki</p>
<h3>Nowość w ofercie!</h3>
<!-- Dobrze -->
<h2>Dane do wysyłki</h2>
<p class="wyroznienie">Nowość w ofercie!</p>
Linki „Czytaj więcej”
Lista linków w czytniku ekranu pokaże wiele razy „Czytaj więcej” bez informacji, czego dotyczą. Kryterium 2.4.4 dopuszcza ustalenie celu linku z otoczenia, np. z tego samego akapitu, ale osobie przeglądającej samą listę linków to nie pomoże. Najlepiej pisz linki opisowe. Jeśli projekt wymaga krótkiego napisu, dodaj ukryte uzupełnienie tak, żeby nazwa linku zawierała widoczny tekst – wymaga tego 2.5.3 Label in Name – etykieta w nazwie (A).
<!-- Słabo -->
<a href="/blog/kontrast">Czytaj więcej</a>
<!-- Lepiej -->
<a href="/blog/kontrast">Czytaj więcej<span class="visually-hidden"> o kontraście kolorów</span></a>
Jak naprawiać najczęstsze błędy WCAG
- Poprawiaj u źródła: w szablonie, komponencie, zmiennej koloru. Jedna zmiana naprawia wtedy błąd na wszystkich podstronach.
- Zacznij od blokad na kluczowych ścieżkach: formularze, koszyk, logowanie, nawigacja klawiaturą.
- Uruchom test automatyczny, żeby znaleźć powtarzalne błędy z listy WebAIM.
- Przejdź najważniejsze ścieżki klawiaturą i czytnikiem ekranu według poradnika jak sprawdzić stronę pod kątem WCAG.
- Zapobiegaj nawrotom: wytyczne o tekstach alternatywnych dla redakcji, kolory sprawdzone w design systemie, kontrola dostępności przed każdym wdrożeniem.
Najczęstsze pytania
Czy nakładka lub wtyczka dostępności usunie te błędy?
Nie. Nakładki typu „jeden skrypt” nie naprawiają kodu strony i nie zapewniają zgodności z WCAG, a mogą kolidować z technologiami asystującymi. Błędy trzeba poprawić w szablonie i treści.
Czy brak błędów w teście automatycznym oznacza, że strona jest zgodna z WCAG?
Nie. Automat sprawdza tylko część wymagań, a sensowność opisów, obsługa klawiaturą czy zrozumiałość komunikatów wymagają ręcznej weryfikacji. Wynik testu automatycznego nie jest certyfikatem ani potwierdzeniem zgodności.
Jak często sprawdzać stronę pod kątem tych błędów?
Najlepiej przy każdej większej zmianie: nowym szablonie, redesignie, nowej wtyczce czy kampanii z nowymi banerami. Część błędów, np. brak tekstów alternatywnych, wraca razem z nowymi treściami, więc warto sprawdzać stronę regularnie.