Цвет и контраст: говорим с пользователем цветом

Цвет — один из сильнейших инструментов интерфейса: он задаёт настроение, направляет взгляд и сообщает о состоянии системы. Но цвет одновременно и риск: без достаточного контраста текст нечитаем, а если полагаться только на цвет, часть пользователей просто не получит сигнал. Разберём модели цвета, научимся строить палитры, записывать их в CSS-переменных и проверять контраст по стандарту WCAG.

Уровень: начальный 12 разделов 1.5–2 часа

1. Цвет не украшение, а информация

Новичку кажется, что цвет нужен «чтобы было красиво». На практике цвет — это канал связи с пользователем. Он сообщает состояние системы раньше, чем пользователь прочитает текст:

Это правило называют «цвет-смысл» (color-as-meaning): у каждого цвета в вашем интерфейсе должна быть одна устойчивая роль, и она не должна меняться от экрана к экрану. Если «Сохранить» зелёное на форме входа, то и на странице настроек успешное действие тоже должно быть зелёным.

Цвет никогда не должен быть единственным носителем смысла. Около 8% мужчин имеют ту или иную форму дальтонизма, а монохромные экраны встречаются не так уж редко. Дублируйте сигнал текстом, иконкой или формой — об этом подробнее в разделе 9.
Сформулируйте для проекта короткий «словарь цвета»: одна строка на роль («красный — только ошибки, зелёный — только успех»). Он убережёт от архаичных решений, когда успех внезапно становится красным.

2. Цветовые модели CSS

В CSS цвет можно записать несколькими способами. Все они задают один и тот же пиксель, но выбирать модель стоит под задачу.

.hex { color: #2563eb; }                     /* HEX: RRGGBB — компактно и привычно */
.rgba { color: rgba(37, 99, 235, 0.5); }     /* r, g, b и прозрачность 0–1 */
.hsl { color: hsl(217, 91%, 60%); }          /* тон, насыщенность, светлота */
.modern { color: hsl(217 91% 60% / 50%); }   /* современный синтаксис с синусами */
.oklch { color: oklch(0.55 0.18 260); }      /* равномерная для глаза модель */
МодельЧто задаётКогда удобна
hexR, G, B в шестнадцатеричной формекопирование из Figma, простая запись
rgb() / rgba()R, G, B плюс прозрачностьработа с прозрачностью
hsl()тон, насыщенность, светлотаперебор палитры: меняете только тон или светлость
oklch() / color()новая модель с равномерным шагомсовременные браузеры, широкий цветовой охват

Почему hsl удобен для палитры? Потому что «тон» и «светлота» разделены. Зелёный светлее или темнее — это просто другая третья координата. В hex те же операции превращаются в непрозрачные коды вида #2563eb → #1e40af, в которых человеческий глаз не видит закономерности.

oklch() и color() поддерживаются не всеми браузерами одинаково. Для учебных и рабочих проектов вполне достаточно hex и hsl — контраст и палитры строятся на них без ограничений.
Любую пару моделей можно перевести в DevTools: в панели Styles кликните на квадратик цвета и переключайте формат (hex, rgb, hsl, oklch). Переводить «вручную» не нужно.

3. Построение палитры

Палитра задаёт «лицо» интерфейса. Минимально рабочая схема состоит из трёх типов цветов:

Классическое соотношение — правило 60-30-10: 60% нейтрального фона, 30% вторичного цвета, 10% сильного акцента. Оно не обязательная истина, но хорошая отправная точка, чтобы интерфейс не «кричал».

Не нужно выдумывать каждый оттенок отдельно. Возьмите один тон и пройдитесь по светлоте — так получаются состояния кнопки:

.btn { background: hsl(217 91% 60%); }    /* обычное состояние */
.btn:hover { background: hsl(217 91% 55%); }  /* чуть темнее на наведении */
.btn:active { background: hsl(217 91% 48%); } /* нажатие — ещё темнее */
.btn:disabled { background: hsl(217 10% 80%); } /* серое и неактивное */

Также удобна функция color-mix() — она смешивает цвет с белым или чёрным без третьей переменной:

.btn:hover { background: color-mix(in srgb, var(--accent) 85%, black); }
Готовые колёса цветов (Coolors, Adobe Color, Paletton) генерируют гармоничные наборы по правилам соседства и дополнения. Это экономит часы подбора вручную — список сервисов в разделе 11.
Гармоничная палитра — это ещё не доступный сайт: красивый жёлтый текст на белом фоне провалит контраст. Эстетику и читаемость проверяйте отдельно (разделы 5–6).

4. CSS-переменные для палитры

Цвета, написанные прямо в каждом правиле, превращают правки в ад: поменять фирменный синий придётся в сорока местах. Правильный путь — вынести палитру в CSS-переменные, объявленные в :root.

:root {
  --accent: #2563eb;          /* основной акцент */
  --accent-dark: #1e40af;     /* hover */
  --accent-light: #bfdbfe;    /* выделение, контур фокуса */
  --success: #16a34a;         /* успех */
  --danger: #dc2626;          /* ошибка */
  --text: #111827;            /* основной текст */
  --text-muted: #6b7280;      /* второстепенный текст */
  --bg: #ffffff;              /* фон */
  --border: #e5e7eb;          /* границы */
}

.btn { background: var(--accent); }
.btn:hover { background: var(--accent-dark); }

Теперь тема меняется в одном месте: переопределяете значения переменных — и весь интерфейс перекрашивается. Это же основа тёмной темы из раздела 10.

Не «прячьте» все цвета в переменные механически: у каждого должна быть роль. Переменная --color-123 хуже явного #2563eb — имя не говорит, что произойдёт при её изменении.
Именуйте переменные по роли, а не по оттенку: --danger вместо --red. Тогда смена фирменного цвета не сломает смыслы: «ошибка» останется «ошибкой», даже если станет оранжевой.

Живой пример: палитра на переменных

Нажмите «Запустить» и попробуйте изменить значения переменной --accent в коде — кнопки и образцы перекрасятся во всём документе сразу.

5. Контраст и WCAG

Контраст — это разница яркости между текстом и его фоном. Документ с контрастом, который не виден на слабом экране или людям со слабым зрением, обречён: его просто не прочитают. WCAG (Web Content Accessibility Guidelines) задаёт формальный критерий — contrast ratio — число от 1 до 21, показывающее отношение светлоты фона и текста.

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

УровеньОбычный текстКрупный текстUI-границы
AA (обязателен)4.5:13:13:1
AAA (желателен)7:14.5:13:1

Типовые ошибки: серый текст «для красоты» на белом фоне (контраст 2:1), тонкие линии ввода с едва заметной границей, светло-жёлтые уведомления с белым текстом.

Крупный текст по WCAG — это от 24px обычного начертания или от 18.66px (примерно 14pt) полужирного. Заголовки обычно проходят по количеству, но проверяйте и их: жирный синий на тёмном фоне тоже умеет проваливаться.
Контраст текста-декорации (подписи, даты, плейсхолдеры) — это тоже контраст. Плейсхолдер поля ввода нередко рисуют на грани видимости: поднимите его до 4.5:1, и форма станет доступнее без изменения дизайна.

6. Проверка контраста

Проверять контраст «на глаз» нельзя — человеческий глаз оценивает светлоту ненадёжно. Используйте инструменты, и они, во-первых, посчитают ratio точно, а во-вторых, избавят от споров в команде.

Один и тот же элемент живёт в разных состояниях: проверяйте не только «обычную» пару, но и hover, фокус, disabled, ошибку. Покрасили кнопку на hover — проверьте текст кнопки заново: фон потемнел, а белый текст мог потерять контраст.
Проверку контраста легко автоматизировать: добавьте Lighthouse или axe в CI — тогда страница с новым «безопасным» серым цветом просто не пройдёт деплой.

7. Крупный текст и UI-границы

WCAG требует контраст 3:1 не только для крупного текста, но и для того, что пользователь должен различить «как элемент управления». Что попадает под требование:

Типичный провал: кнопка с белым фоном, серой границей border: 1px solid #ccc и белым текстом. Граница против фона — это 1.2:1, кнопка «исчезает». Исправление — затемнить границу или добавить тень.

/* Плохо: границу почти не видно */
.btn { border: 1px solid #d1d5db; color: #ffffff; background: #ffffff; }

/* Хорошо: граница контрастирует с фоном, текст читается */
.btn { border: 1px solid #6b7280; color: #111827; background: #ffffff; }
Состояние focus обязано быть различимым — иначе клавиатурным пользователям не видно, где они находятся. Серый фокус на сером фоне не считается: добавьте контрастный outline (обычно 3:1) и отступ между рамкой и соседями.
Не делайте «уникальные» границы для всех кнопок разом: интерактивный элемент должен отличаться либо толстым контуром, либо заливкой, либо тенью. Одного бледного углубления недостаточно.

8. Цвет состояния

Интерактивный элемент проходит через состояния: обычное, hover (наведение), focus (клавиатура), active (нажатие), disabled (отключено). Для каждого состояния цвет может меняться, но правило из раздела 1 работает и здесь: состояние не должно передаваться только цветом.

.btn {
  background: var(--accent);
  color: #fff;
  padding: 10px 18px;
  border: 0;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
}
.btn:hover {
  background: var(--accent-dark);
  box-shadow: 0 0 0 3px var(--accent-light); /* дополнительный сигнал: кольцо */
}
.btn:active {
  background: hsl(217 91% 45%);
  transform: translateY(1px);              /* нажатие заметно и без цвета */
}
.btn:focus-visible {
  outline: 3px solid #f59e0b;              /* фокус клавиатурой — контрастное кольцо */
  outline-offset: 2px;
}
.btn:disabled {
  background: var(--border);
  color: var(--text-muted);
  cursor: not-allowed;                     /* курсор тоже сигнал */
}

Обратите внимание на приёмы: тень-кольцо на hover, сдвиг на нажатии, контрастный outline на фокусе. Цвет дополняет тактильные и пространственные подсказки, а не заменяет их.

Состояние disabled выглядит «стёртым» — это нормально, но не скрывайте, почему кнопка не нажимается. Добавьте подсказку-подпись (title или текст рядом), иначе пользователь будет безуспешно кликать и подумает, что сайт сломался.

Живой пример: состояния с дополнительными сигналами

Прогоните курсором по кнопкам, щёлкните и нажмите Tab с клавиатурой: каждое состояние меняет не только цвет, но и тень, сдвиг или контур.

9. Цвет слепоты: не только цвет

Дальтонизм (нарушение цветовосприятия) встречается примерно у 8% мужчин и 0.5% женщин. Самые частые формы — дейтераномалия и протанопия (сложности с красным и зелёным). Ровно те цвета, которыми помечают «успех» и «ошибку», люди с этими формами различают хуже всего.

Правило простое: рядом с цветом всегда должен быть второй сигнал, который не зависит от зрения.

/* Плохо: только цвет говорит об ошибке */
.valid   { border-color: green; }
.invalid { border-color: red; }

/* Хорошо: цвет + текст + иконка */
.valid::after   { content: "OK"; }
.invalid::after { content: "! Заполните поле"; }

Какие сигналы дублируют цвет:

Пример валидации формы: вместо «поле просто стало красным» сделайте так, чтобы слева появился ярлык «Заполните поле» с иконкой и цветной рамкой. Тогда форма понятна и дальтонику, и человеку с монохромным экраном, и скринридеру.

Диаграммы тоже под запретом «только цвет»: линии графика различайте не только оттенком (синяя против зелёной), но и типом — пунктирная, точечная, толстая. Обязательная надпись или легенда рядом с каждой серией.
Быстрая самопроверка: включите в DevTools эмуляцию дальтонизма (Rendering → Emulate CSS media feature) или поставьте расширение Silvertrac/Nocoffee. Если интерфейс «понимается» и с ними — вы на правильном пути.

10. Тёмная тема

Тёмная тема — это не «инверсия цветов», а отдельная палитра. Записать её проще всего переопределением CSS-переменных внутри медиазапроса prefers-color-scheme: dark — подписчика на системные настройки пользователя.

:root {
  --bg: #ffffff;
  --text: #111827;
  --text-muted: #6b7280;
  --accent: #2563eb;
  --border: #e5e7eb;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;              /* тёмный фон, но не чисто-чёрный */
    --text: #f1f5f9;
    --text-muted: #94a3b8;      /* серый на тёмном читается хуже — светлее */
    --accent: #60a5fa;          /* акцент светлеет, чтобы контраст держался */
    --border: #334155;
  }
}

body {
  background: var(--bg);
  color: var(--text);
}

Три типовые ошибки тёмных тем:

:root { color-scheme: light dark; }   /* браузер знает, что тема есть */
html { background: var(--bg); }       /* фон до загрузки — без белой вспышки */
Если рядом с тёмной темой есть переключатель вручную, синхронизируйте его с предпочтениями системы через matchMedia в JavaScript. Переключение — это отдельная кнопка, а автоматический выбор — не менее важный.
Проверьте тёмную тему теми же инструментами контраста, что и светлую: пара «акцент на фоне» в тёмной версии — это другие числа, и они считаются заново.

11. Инструменты и библиотеки палитр

Готовых сервисов много: часть помогает собрать гармоничную палитру, часть — проверить её доступность. Держите под рукой короткий список.

СервисНазначение
WebAIM Contrast Checkerрасчёт contrast ratio и вердикт по AA/AAA для пары цветов
axe DevToolsлинтинг доступности прямо в браузере, включая контраст
Lighthouseаудит страницы: доступность, производительность, SEO
Coolorsбыстрый генератор гармоничных палитр с блокировкой оттенков
Adobe Colorправила цветовых гармоний и извлечение цветов из картинки
Palettonпродуманное колесо для монохромных и комплементарных схем
Material Design Color Toolзадача из одной палитры, варианты оттенков для UI

Как работать со «сгенерированной» палитрой: приносите её в проект как отправную точку, раскладываете по ролям (основной, акцент, нейтральные) в переменные из раздела 4 и — ключевой шаг — прогоняете все пары через проверку контраста. Генераторы не знают, на каком фоне будет ваш текст.

Не доверяйте палитре «на глаз» только потому, что она из красивого сервиса: bright yellow на белом все равно провалит контраст. Правило звучит жёстко: берите палитру, но проверяйте контраст сами.
Храните ссылку на проверенный набор в файле проекта (например, в README или в комментарии в CSS у :root): преемник поймёт, откуда взялись цвета и почему они такие.

12. Чек-лист перед сдачей

Финальная сверка перед тем, как показать страницу заказчику или коллеге. Прогоните пункты по порядку — они покрывают всё, о чём шла речь в уроке.

Последний пункт коварен: многие фреймворки по умолчанию рисуют галочку цветом, который на фоне кнопки почти неразличим. Проверьте собственный чекбокс в «выбранном» и «пустом» виде.
Прогоняйте чек-лист за 10 минут до сдачи и ещё раз на следующий день: свежий взгляд находит «серые» приёмы, на которые «привыкший» глаз уже не реагирует.

Что дальше

Ты разобрался с цветом, контрастом и доступностью — теперь интерфейс не только аккуратный, но и читаемый для всех. Вернись к базовым свойствам в курсе CSS, прокачай оформление текста в уроке Веб-типографика, а затем собери всё вместе в Практике: лендинг — там подручные палитры и состояния применяются к полноценной странице.

← Вернуться к курсам