Как проверить контраст цветов по WCAG
Практика4 минРедакция Innovialabs
Коротко
По WCAG 2 обычному тексту нужен контраст не ниже 4.5:1 (AA) или 7:1 (AAA), крупному — 3:1 или 4.5:1, элементам интерфейса и значимой графике — 3:1; проверяйте каждую реальную пару текста и фона и затемняйте или увеличивайте то, что не проходит.
Коротко о главном
Коэффициент контраста сравнивает относительную яркость двух цветов по шкале от 1:1 (одинаковые) до 21:1 (чёрный на белом). WCAG 2 задаёт такие минимумы:
| Что | AA | AAA |
|---|---|---|
| Обычный текст | 4.5:1 | 7:1 |
| Крупный текст | 3:1 | 4.5:1 |
| Элементы интерфейса и графика | 3:1 | отдельно не задано |
Крупный текст — от 18 pt (около 24 px) обычного начертания или от 14 pt (около 18,5 px) жирного. Большинство законов и договоров, ссылающихся на WCAG, требуют уровня AA.
Значения не округляются: 4.49:1 не проходит требование 4.5:1.
Что именно должно проходить проверку
Текст (критерий 1.4.3 для AA, 1.4.6 для AAA):
- основной текст, подписи, плейсхолдеры со смыслом, ссылки;
- текст на кнопках, бейджах, подсказках;
- текст на изображениях и градиентах — в самом светлом месте под ним.
Нетекстовый контраст (1.4.11, AA) — 3:1 относительно соседних цветов:
- рамки полей ввода, если только рамка показывает границы поля;
- контуры чекбоксов и радиокнопок, состояния переключателей;
- индикаторы фокуса;
- иконки, которые несут смысл без текстовой подписи;
- значимые части графиков: линии, сегменты.
Исключения: неактивные элементы, чистый декор, логотипы и названия брендов, невидимый текст и текст, который является частью картинки с другим важным содержанием.
Как считается коэффициент
Каждый цвет переводится в относительную яркость L, затем:
ratio = (L_светлее + 0.05) / (L_темнее + 0.05)
function luminance(hex) {
const [r, g, b] = hex.match(/\w\w/g).map((h) => {
const c = parseInt(h, 16) / 255;
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function contrast(a, b) {
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05);
}
contrast("#767676", "#ffffff"); // около 4.54 — проходит AA для обычного текста
Считать вручную приходится редко, но формула объясняет, почему «средний» серый часто не проходит.
Инструменты для проверки
В браузере:
- WebAIM Contrast Checker: вводите два цвета, получаете коэффициент и результат для каждого уровня.
- DevTools браузера: пипетка в панели стилей показывает контраст выбранного текста.
- Lighthouse и axe DevTools: автоматический аудит, который находит текст с низким контрастом на всей странице.
На компьютере:
- Colour Contrast Analyser от TPGi: пипетка для любого пикселя на экране, удобна для картинок и градиентов.
В Figma:
- Плагины Stark, Contrast, A11y – Color Contrast Checker проверяют выбранные слои или целые фреймы.
- Порядок работы: проверяйте состояния компонентов в библиотеке (обычное, hover, неактивное, ошибка), а не только готовые экраны.
Автоматические инструменты плохо оценивают текст на фотографиях. Такие места проверяйте вручную пипеткой по самому светлому пикселю за текстом.
Как исправить брендовые цвета, которые не проходят
Брендовые цвета часто яркие и не проходят как текст на белом. Менять бренд не обязательно — меняйте способ использования цвета.
- Более тёмная ступень для текста и ссылок. Яркий цвет оставьте для крупных плоскостей и декора, а для текста возьмите более тёмный оттенок того же тона.
- Смените цвет текста. Если белый текст на яркой кнопке не проходит, тёмный на том же фоне может пройти.
- Сделайте текст крупным. Заголовку крупного размера на уровне AA достаточно 3:1. Для основного текста этот приём не подходит.
- Добавьте второй признак. Подчёркивайте ссылки, добавляйте рамки или иконки, чтобы смысл не держался только на цвете.
- Используйте подложки на изображениях. Тёмный градиент под текстом на фото делает контраст предсказуемым.
- Заведите доступные токены. Определите проверенные
text-brand,bg-brand,border-brand, чтобы дизайнеры не брали «сырой» цвет.
Частые ошибки
- Светло-серый плейсхолдер вместо подписи поля.
- Проверка только обычного состояния без hover, фокуса и ошибки.
- Уверенность, что прошедшее в светлой теме пройдёт и в тёмной.
- Тонкие начертания в мелком кегле: формально проходят, но читаются хуже.