Kontrast kolorów WCAG to jedno z niewielu wymagań dostępności, które da się sprawdzić liczbą: proste progi określają, czy tekst i elementy interfejsu są wystarczająco odróżnialne od tła. To zarazem jeden z najczęstszych problemów – według raportu WebAIM Million niski kontrast tekstu od lat jest najczęściej wykrywanym automatycznie błędem na stronach głównych.
Dobry kontrast pomaga osobom słabowidzącym, starszym i z zaburzeniami widzenia barw, ale też każdemu, kto czyta z telefonu w pełnym słońcu. Poniżej znajdziesz progi, wzór, przykładowe pary kolorów i pułapki, na których najczęściej potykają się projekty.
Kontrast kolorów WCAG: trzy kryteria i ich progi
Kontrastu dotyczą trzy kryteria sukcesu. Dwa mówią o tekście, trzecie o elementach, które tekstem nie są.
| Kryterium | Poziom | Zwykły tekst | Duży tekst | Komponenty i grafiki |
|---|---|---|---|---|
| 1.4.3 Contrast (Minimum) – kontrast minimalny | AA | 4,5:1 | 3:1 | nie dotyczy |
| 1.4.6 Contrast (Enhanced) – kontrast wzmocniony | AAA | 7:1 | 4,5:1 | nie dotyczy |
| 1.4.11 Non-text Contrast – kontrast elementów nietekstowych | AA | nie dotyczy | nie dotyczy | 3:1 |
1.4.3 to podstawa dla każdej strony, która celuje w poziom AA. Wymóg obejmuje cały tekst, również tekst zastępczy w polach formularzy i tekst widoczny dopiero po najechaniu kursorem lub przy fokusie. Wyjątki to logotypy i nazwy marek, tekst czysto dekoracyjny, tekst nieaktywnych (wyłączonych) elementów oraz napisy będące tylko fragmentem zdjęcia, np. szyld na fotografii ulicy.
1.4.6 to poziom AAA, zwykle niewymagany. Jeśli jednak projektujesz od zera, kolor tekstu podstawowego z kontrastem 7:1 nic nie kosztuje.
1.4.11 dotyczy komponentów interfejsu i ich stanów: obramowania pola, jeśli to ono pokazuje, gdzie jest pole, ikon pełniących funkcję przycisków, znacznika w polu wyboru, wskaźnika fokusu. Obejmuje też elementy grafik potrzebne do zrozumienia treści, np. linie i słupki na wykresie. Wyjątkiem są nieaktywne komponenty i kontrolki w domyślnym wyglądzie przeglądarki. Jasnoszare obramowanie #CCCCCC na białym tle ma tylko 1,61:1 – pole formularza praktycznie znika.
Co to jest duży tekst
Niższy próg (3:1 na poziomie AA, 4,5:1 na poziomie AAA) dotyczy wyłącznie dużego tekstu. WCAG uznaje za duży tekst o rozmiarze co najmniej 18 punktów, czyli ok. 24 px, albo co najmniej 14 punktów, jeśli jest pogrubiony – to ok. 18,66 px.
Liczy się rzeczywisty rozmiar wyrenderowany w przeglądarce, a nie wrażenie „dużego” napisu. Nagłówek o wielkości 20 px i normalnej grubości to w rozumieniu WCAG zwykły tekst, który potrzebuje 4,5:1. Przy krojach o bardzo cienkich liniach zostaw sobie zapas, bo nawet spełniony próg nie gwarantuje czytelności.
Wzór na kontrast i luminancja w prostych słowach
Współczynnik kontrastu liczy się ze wzoru (L1 + 0,05) / (L2 + 0,05), gdzie L1 to względna luminancja jaśniejszego koloru, a L2 – ciemniejszego. Wynik mieści się w przedziale od 1:1 (dwa identyczne kolory) do 21:1 (czerń na bieli).
Względna luminancja opisuje, jak jasny wydaje się kolor ludzkiemu oku – w skali od 0 dla czerni do 1 dla bieli. Składowe R, G i B przelicza się na wartości liniowe i sumuje z wagami: zieleń waży najwięcej, niebieski najmniej. Dlatego żółty tekst na białym tle jest prawie nieczytelny, a granatowy bardzo wyraźny, choć oba kolory są „mocne”.
Jeśli chcesz sprawdzać kontrast w testach design systemu, wzór łatwo zapisać w kodzie (kolory w formacie #RRGGBB):
function luminancja(hex) {
const [r, g, b] = [1, 3, 5].map((i) => {
const c = parseInt(hex.slice(i, i + 2), 16) / 255;
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function kontrast(kolor1, kolor2) {
const [jasny, ciemny] = [luminancja(kolor1), luminancja(kolor2)].sort((a, b) => b - a);
return (jasny + 0.05) / (ciemny + 0.05);
}
kontrast("#767676", "#ffffff"); // 4.54…
Progi traktuj dosłownie: 4,48:1 to wciąż mniej niż 4,5:1, nawet jeśli narzędzie zaokrągli wynik do jednego miejsca po przecinku.
Przykładowe pary kolorów i wyniki
Wartości w tabeli policzono według wzoru WCAG i zaokrąglono do dwóch miejsc po przecinku. Zwróć uwagę, jak niewiele dzieli #767676 od #777777 i jak ten sam niebieski zachowuje się na jasnym i ciemnym tle.
| Kolor tekstu lub elementu | Tło | Kontrast | Zwykły tekst AA (4,5:1) | Duży tekst i komponenty (3:1) |
|---|---|---|---|---|
| #595959 | #FFFFFF | 7,00:1 | tak (również AAA) | tak |
| #767676 | #FFFFFF | 4,54:1 | tak | tak |
| #777777 | #FFFFFF | 4,48:1 | nie | tak |
| #949494 | #FFFFFF | 3,03:1 | nie | tak |
| #999999 | #FFFFFF | 2,85:1 | nie | nie |
| #FFFFFF | #FF7A00 | 2,61:1 | nie | nie |
| #000000 | #FF7A00 | 8,04:1 | tak | tak |
| #FFFFFF | #B34700 | 5,50:1 | tak | tak |
| #FFFFFF | #0066CC | 5,57:1 | tak | tak |
| #0066CC | #121212 | 3,37:1 | nie | tak |
| #4DA3FF | #121212 | 7,14:1 | tak | tak |
Typowe pułapki
Tekst na zdjęciu lub gradiencie
Na zdjęciu kontrast zmienia się w każdym punkcie, a liczy się najsłabsze miejsce pod literami. Kadr przesuwa się też przy różnych szerokościach ekranu, więc napis czytelny na komputerze może na telefonie trafić na jasne niebo. Pomaga półprzezroczysta ciemna warstwa pod tekstem, jednolite tło pod samym napisem albo przeniesienie tekstu poza zdjęcie.
Placeholder
Tekst zastępczy w polach bywa bardzo jasny – #A9A9A9 na bieli to tylko 2,35:1. Placeholder też jest tekstem i potrzebuje 4,5:1. Nie zastępuje przy tym etykiety pola, bo znika, gdy zaczynasz pisać.
Stany hover i focus
Każdy stan elementu musi spełniać progi osobno. Częste błędy to link, który po najechaniu jaśnieje, i przycisk, który na hover dostaje jaśniejsze tło przy białym napisie. Sam wskaźnik fokusu to element nietekstowy – potrzebuje co najmniej 3:1 względem sąsiednich kolorów. O nowych wymaganiach dotyczących fokusu przeczytasz w artykule o WCAG 2.2.
Przyciski w kolorze marki
Jasne kolory marki – pomarańczowe, żółte, jasnozielone – zwykle nie działają z białym napisem: biel na #FF7A00 daje 2,61:1. Masz trzy wyjścia: przyciemnić tło przycisku (biel na #B34700 to 5,50:1), zmienić napis na ciemny (czerń na #FF7A00 to 8,04:1) albo zostawić jasny kolor marki dla elementów dekoracyjnych. Próg 3:1 dotyczy tylko naprawdę dużego tekstu, a napisy na przyciskach rzadko są tak duże.
Tryb ciemny
Ciemny motyw to osobna paleta, którą trzeba sprawdzić od nowa. Niebieski #0066CC jako tło przycisku z białym napisem ma 5,57:1, ale jako kolor linku na tle #121212 – tylko 3,37:1. Najbezpieczniej trzymać kolory w zmiennych i definiować osobne wartości dla każdego motywu:
:root {
--kolor-linku: #0066cc;
}
@media (prefers-color-scheme: dark) {
:root {
--kolor-linku: #4da3ff; /* 7,14:1 na tle #121212 */
}
}
Uważaj też na przezroczystość (opacity, rgba): wynikowy kolor tekstu zależy od tła, na którym leży, więc ten sam styl może przejść test w jednym miejscu, a w innym nie.
Jak sprawdzić kontrast WCAG na stronie
Narzędzia deweloperskie przeglądarki. W Chrome i Edge kliknij próbkę koloru przy właściwości color w panelu stylów – selektor kolorów pokaże współczynnik kontrastu z progami AA i AAA. Firefox w panelu dostępności (Accessibility) potrafi wyszukać wszystkie elementy z problemem kontrastu.
Kalkulator kontrastu. Do sprawdzenia pary kolorów z projektu wystarczy kalkulator, np. WebAIM Contrast Checker. Wybieraj narzędzia, które pokazują wynik z dokładnością do dwóch miejsc po przecinku.
Test automatyczny. Skaner sprawdzi kontrast tekstu na wielu podstronach naraz i wskaże konkretne elementy, dzięki czemu szybko znajdziesz problemy powtarzające się w szablonie. Jak czytać wyniki takiego skanu, opisujemy w artykule test WCAG online.
Ręczna weryfikacja. Przy tekście na zdjęciach, gradientach i nakładających się warstwach automat zwykle nie potrafi ustalić koloru tła i oznacza element do sprawdzenia przez człowieka. Dlatego raport z testu automatycznego powinien zawierać listę elementów do ręcznej weryfikacji – taką listę ma m.in. Raport Podstawowy w Cyfrowo Dostępni. Te miejsca sprawdź pipetą w kilku szerokościach ekranu, razem ze stanami hover i focus. Pozostałe kroki ręcznej kontroli znajdziesz w poradniku jak sprawdzić stronę pod kątem WCAG.
Lista kontrolna kontrastu
- Tekst podstawowy, linki i napisy na przyciskach: co najmniej 4,5:1.
- Duży tekst (od ok. 24 px albo od ok. 18,66 px pogrubiony): co najmniej 3:1.
- Obramowania pól, ikony, wskaźnik fokusu i elementy wykresów: co najmniej 3:1.
- Każdy stan elementu: domyślny, hover, focus, błąd.
- Oba motywy: jasny i ciemny.
- Tekst na zdjęciach sprawdzony ręcznie w kilku szerokościach ekranu.
Niski kontrast to tylko jeden z typowych problemów – pozostałe opisujemy w zestawieniu najczęstszych błędów WCAG.
Najczęstsze pytania
Czy linki w tekście muszą różnić się od otoczenia czymś więcej niż kolorem?
Co do zasady tak, bo kryterium 1.4.1 Use of Color nie pozwala przekazywać informacji wyłącznie kolorem. Najprościej zostawić podkreślenie linków. Jeśli z niego rezygnujesz, link potrzebuje kontrastu co najmniej 3:1 względem otaczającego tekstu i dodatkowego wyróżnienia po najechaniu i przy fokusie.
Czy przycisk „wysoki kontrast” w nakładce dostępności rozwiązuje problem?
Nie. Nakładki typu „jeden skrypt” nie naprawiają kodu strony i nie zapewniają zgodności z WCAG, a mogą kolidować z technologiami asystującymi. Najpewniejsza droga to kolory, które spełniają progi w podstawowym wyglądzie strony.
Czy wymagania kontrastu dotyczą tekstu wtopionego w grafikę, np. na banerze?
Tak. Kryterium 1.4.3 obejmuje także obrazy tekstu, chyba że napis jest częścią logo. Automat zwykle nie zmierzy takiego kontrastu wiarygodnie, więc sprawdź go ręcznie pipetą w najjaśniejszym i najciemniejszym miejscu pod literami.