Зачем нужен цветовой контраст: от катаракты до яркого солнца

Доступность интерфейсов (accessibility, a11y) часто ошибочно воспринимают как заботу исключительно о тотально незрячих людях со скринридерами. Однако контрастность цветов напрямую затрагивает подавляющее большинство пользователей:

Стандарт WCAG 2.2 (Web Content Accessibility Guidelines, официальная рекомендация консорциума W3C с октября 2023 года) закрепляет строгие математические требования к контрасту, признанные международным стандартом ISO/IEC 40500 и законодательствами ЕС (European Accessibility Act) и США (ADA Title III).

Нормативы WCAG 2.2: уровни AA и AAA

В стандарте WCAG предусмотрено три уровня соответствия: A (минимальный), AA (отраслевой стандарт для всех коммерческих и государственных сайтов) и AAA (повышенный уровень для специализированных сервисов). Для визуального оформления критичны уровни AA и AAA:

Элемент интерфейса Размер / Определение Уровень AA (обязательный) Уровень AAA (повышенный)
Обычный текст Меньше 24px (regular) или меньше 18.5px (bold) 4.5 : 1 7.0 : 1
Крупный текст От 24px (regular) или от 18.5px (bold) 3.0 : 1 4.5 : 1
Нетекстовые элементы (SC 1.4.11) Границы инпутов, чекбоксы, графики, иконки действий 3.0 : 1 —
Индикатор клавиатурного фокуса (SC 2.4.13) Рамка вокруг активного интерактивного элемента 3.0 : 1 —

Исключения: логотипы и товарные знаки, чисто декоративные фоновые иллюстрации и элементы, находящиеся в отключённом неактивном состоянии (атрибут disabled), освобождены от требований контрастности.

Математика контраста: относительная яркость и формула

Как именно вычисляется коэффициент контраста? Он основан на понятии относительной яркости (Relative Luminance, $L$), нормированной от 0.0 для абсолютно чёрного цвета до 1.0 для идеального белого.

Сначала значения цветовых каналов $sRGB$ пересчитываются из нелинейного гамма-пространства в линейную оптическую яркость. Для каждого канала ($R, G, B \in [0, 255]$):

C_srgb = C / 255
C_linear = (C_srgb <= 0.04045) ? (C_srgb / 12.92) : Math.pow((C_srgb + 0.055) / 1.055, 2.4)

Затем вычисляется относительная яркость $L$ с физиологическими весовыми коэффициентами, отражающими чувствительность рецепторов человеческого глаза к разным длинам волн:

L = 0.2126 * R_linear + 0.7152 * G_linear + 0.0722 * B_linear

Зелёный цвет даёт свыше 71.5% воспринимаемой глазом яркости, красный — 21.3%, а синий — лишь 7.2%. Вот почему чистый синий текст #0000FF на чёрном фоне практически не читается, хотя имеет максимальное цифровое значение в канале цвета!

Итоговый коэффициент контраста между более светлым ($L_1$) и более тёмным ($L_2$) цветом вычисляется по формуле:

Contrast Ratio = (L1 + 0.05) / (L2 + 0.05)

Константа +0.05 введена W3C для учёта окружающего рассеянного света (ambient flare), отражающегося от стекла экрана при реальном освещении. Коэффициент варьируется от 1:1 (одинаковые цвета) до 21:1 (абсолютно белый против абсолютно чёрного).

WCAG 2 против APCA: почему старая формула ошибается

Формула WCAG 2 была разработана в 2008 году. За прошедшие годы исследователи зрительного восприятия выявили в ней ряд фундаментальных изъянов:

Новый алгоритм APCA (Accessible Perceptual Contrast Algorithm), разрабатываемый для будущего стандарта WCAG 3, рассчитывает показатель воспринимаемого контраста (обозначается как $L^c$). В APCA минимально необходимый балл $L^c$ напрямую привязан к матрице «размер шрифта × толщина штриха»: чем тоньше шрифт, тем более высокий физический контраст требуется.

Нетекстовый контраст и рамки фокуса (SC 1.4.11 и SC 2.4.13)

Одна из наиболее частых причин штрафов и судебных претензий по доступности — ошибки в интерактивных элементах управления:

Критерий SC 1.4.11 (Non-text Contrast)

Все визуальные границы, позволяющие понять форму и состояние компонента, обязаны иметь контраст не менее 3.0:1 относительно фона страницы.

Типичная ошибка дизайнеров: рисовать рамку текстового инпута модным светло-серым цветом #E2E8F0 на белом фоне #FFFFFF. Контраст этой границы составляет всего 1.3:1 — такой инпут буквально растворяется на экране для слабовидящих пользователей. Используйте для границ нейтральные оттенки с контрастом от 3:1 (например, #94A3B8 или темнее).

Критерий SC 2.4.13 (Focus Appearance в WCAG 2.2)

В стандарте WCAG 2.2 появился новый критерий уровня AA, регламентирующий видимость клавиатурного фокуса при навигации клавишей Tab:

/* КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО: */
button:focus { outline: none; }

/* ПРАВИЛЬНАЯ РЕАЛИЗАЦИЯ ПО WCAG 2.2: */
button:focus-visible {
  outline: 2px solid #06b6d4;
  outline-offset: 2px;
}

Специфика контраста в темной и светлой теме

Создание тёмной темы — это не просто инверсия цветов. В тёмном окружении действуют специфические оптические законы:

Построение доступной дизайн-системы: токены и правило 60-30-10

Чтобы веб-приложение всегда соответствовало стандарту без ручной проверки каждого пикселя, спроектируйте систему семантических токенов:

Семантический токен Светлая тема (Light) Тёмная тема (Dark) Гарантированный контраст
--text-primary #0f172a (Slate 900) #f8fafc (Slate 50) > 15 : 1 (с запасом под AAA)
--text-muted #475569 (Slate 600) #94a3b8 (Slate 400) > 4.6 : 1 (проходит AA)
--border-control #64748b (Slate 500) #94a3b8 (Slate 400) > 3.2 : 1 (проходит SC 1.4.11)
--accent-focus #0284c7 (Sky 600) #38bdf8 (Sky 400) > 3.5 : 1 (проходит SC 2.4.13)

Критическое правило WCAG SC 1.4.1 (Use of Color): цвет никогда не должен быть единственным средством передачи информации, индикации действия или ошибки. Ошибку в поле формы необходимо сопровождать понятным текстовым сообщением и иконкой (⚠️), активную вкладку — подчеркиванием или меткой, а статус графика — контрастными паттернами штриховки в дополнение к цветовой заливке.

👁️ Проверьте контраст любых цветов, симулируйте интерфейс и сгенерируйте гармоничные доступные палитры:
Открыть проверку контрастности WCAG 2.2→

Частые вопросы

Какой минимальный контраст требуется для обычного текста по WCAG 2.2?

Для базового уровня соответствия Level AA минимальный коэффициент контраста составляет 4.5:1 для обычного текста (до 24px regular или до 18.5px bold). Для крупного текста (от 24px regular или от 18.5px bold) порог снижен до 3:1. Для наивысшего уровня AAA требования повышаются до 7:1 и 4.5:1 соответственно.

Нужно ли обеспечивать контраст 3:1 для границ полей ввода и кнопок?

Да. Критерий WCAG 2.2 SC 1.4.11 (Non-text Contrast) требует контраст не менее 3:1 между границей интерактивного элемента (инпута, чекбокса, вкладки) и окружающим фоном, если граница критична для распознавания формы компонента.

В чём принципиальное отличие алгоритма APCA от формулы WCAG 2?

Формула WCAG 2 статична и симметрична: она не учитывает толщину шрифта, его кегль и полярность (белое на черном читается иначе, чем черное на белом из-за рассеяния света в глазу). APCA (Accessible Perceptual Contrast Algorithm) рассчитывает воспринимаемый контраст (Lc) с учетом анатомии человеческого зрения и пространственной частоты.

Почему чистый черный (#000000) фон с белым (#FFFFFF) текстом утомляет глаза?

Контраст 21:1 в условиях низкой освещенности вызывает эффект гало (ореола вокруг букв) и зрительное утомление, особенно у пользователей с астигматизмом. В профессиональных дизайн-системах используют темно-серый фон (#121212 или #0f172a) с приглушенным белым текстом (#e2e8f0), обеспечивающим контраст ~15:1.

Каковы новые требования к рамкам фокуса (Focus Appearance) в WCAG 2.2?

Критерий SC 2.4.13 Level AA требует, чтобы индикатор фокуса имел площадь не менее периметра толщиной 2px вокруг компонента и контраст не менее 3:1 как по сравнению с состоянием без фокуса, так и относительно окружающего фона. Свойство outline: none без замены является прямым нарушением.