Палитра выглядит удачной на макете, а на телефоне под солнцем серый текст исчезает. Дело не во вкусе: у контраста есть измеримая величина и формальные пороги. Если их не учитывать, часть посетителей просто не прочитает страницу.
Почему «красиво» не значит «читаемо»
Человек с нормальным зрением различает оттенки, которые при сниженной контрастной чувствительности сливаются. Сюда же добавляются блики на экране, дешёвые матрицы и масштабирование. Поэтому доступность опирается не на восприятие дизайнера, а на отношение яркостей.
Отдельная ловушка — тёмная тема. Светло-серый #8b8b8b на почти чёрном фоне кажется мягким и «премиальным», но по цифрам может не дотягивать до минимума. Глаз адаптируется, а формальная проверка — нет.
Как считается контраст
Берутся относительные яркости двух цветов, от 0 до 1, и подставляются в формулу:
(L1 + 0.05) / (L2 + 0.05)
Где L1 — яркость более светлого цвета, L2 — более тёмного. Результат от 1 до 21. Чем больше число, тем выше контраст. Белый на чёрном даёт 21, одинаковые цвета — 1.
Яркость канала считается нелинейно: сначала значение делится на 255, затем применяется гамма-коррекция. Простое среднее арифметическое RGB даст неверный результат, особенно на насыщенных цветах.
const channel = (v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
};
function luminance(hex) {
const n = parseInt(hex.replace('#', ''), 16);
const r = channel((n >> 16) & 255);
const g = channel((n >> 8) & 255);
const b = channel(n & 255);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function contrast(a, b) {
const [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
return (l1 + 0.05) / (l2 + 0.05);
}
console.log(contrast('#8b8b8b', '#1e1e1e').toFixed(2));
Пороговые значения
WCAG 2.2 задаёт два уровня, которые чаще всего требуют от сайтов:
- AA: 4.5:1 для обычного текста и 3:1 для крупного;
- AAA: 7:1 для обычного и 4.5:1 для крупного.
Крупным считается текст от 24 пикселей обычного начертания или от 18.66 пикселя полужирного. Порог 3:1 действует и для элементов интерфейса: границ полей, иконок, индикатора фокуса.
Для декоративных надписей и логотипов исключения есть, но злоупотреблять ими не стоит: пользователь всё равно пытается прочитать текст.
Прозрачность, градиенты и картинки
Полупрозрачный белый текст на фотографии не имеет одного значения контраста: он зависит от пикселя под ним. Проверять нужно худший участок, а не средний. Практичное решение — затемняющая подложка с достаточной непрозрачностью или отдельная плашка под текстом.
Если текст кладётся на фотографию, фон лучше подготовить заранее: убрать лишнее через удаление фона и подложить ровный цвет.
С градиентами та же история. Кнопка с переходом от светлого к тёмному может пройти проверку в начале и провалиться в конце. Смотрите оба края и середину.
Тонкие линии и разделители тоже важны: граница поля ввода с контрастом 1.4 видна на макете, но не видна на ноутбуке при ярком свете. Для границ интерфейса ориентируйтесь на 3:1.
Цвет как единственный сигнал
Ошибка в форме, отмеченная только красным, недоступна части пользователей. Добавьте текст, иконку или подчёркивание. Это требование 1.4.1 «Использование цвета» и здравый смысл одновременно.
То же с ссылками в тексте: если они отличаются только цветом и он близок к основному, ссылку трудно найти. Подчёркивание решает проблему без потери контраста.
Как проверить свою палитру
Соберите основные пары «текст — фон»: основной текст, второстепенный, ссылка, кнопка, поле ввода, фокус. Для каждой посчитайте контраст. На сайте есть генератор палитр: он показывает оттенки и помогает подобрать цвета, но финальную проверку лучше делать по формуле выше или в инструментах разработчика.
В браузере откройте DevTools, выберите элемент и посмотрите вычисленный цвет. Не проверяйте по скриншоту: сжатие и цветовой профиль меняют значения. И помните, что rgba(255, 255, 255, 0.6) на фоне #1e1e1e — это не то же самое, что #a0a0a0.
Про APCA и будущее WCAG 3
Формула WCAG 2.x критикуется за то, что плохо описывает тёмные темы и тонкие шрифты. В проекте WCAG 3 предлагается метрика APCA, учитывающая размер и начертание. Пока стандарт не принят, ориентируйтесь на WCAG 2.2 AA: этого требуют аудиты и государственные закупки.
Коротко
- контраст считается по относительной яркости, а не по среднему RGB;
- AA: 4.5:1 для текста, 3:1 для крупного текста и интерфейса;
- проверяйте худший участок на фото и градиентах;
- не передавайте смысл только цветом;
- тёмная тема требует отдельной проверки, а не копирования светлой.