Цвет и контраст: говорим с пользователем цветом
Цвет — один из сильнейших инструментов интерфейса: он задаёт настроение, направляет взгляд и сообщает о состоянии системы. Но цвет одновременно и риск: без достаточного контраста текст нечитаем, а если полагаться только на цвет, часть пользователей просто не получит сигнал. Разберём модели цвета, научимся строить палитры, записывать их в CSS-переменных и проверять контраст по стандарту WCAG.
1. Цвет не украшение, а информация
Новичку кажется, что цвет нужен «чтобы было красиво». На практике цвет — это канал связи с пользователем. Он сообщает состояние системы раньше, чем пользователь прочитает текст:
- красный — ошибка, опасность, удаление;
- зелёный — успех, сохранено, открыто;
- жёлтый/оранжевый — предупреждение, внимание;
- синий — ссылки, действие, информация;
- серый — неактивно, второстепенное, фон.
Это правило называют «цвет-смысл» (color-as-meaning): у каждого цвета в вашем интерфейсе должна быть одна устойчивая роль, и она не должна меняться от экрана к экрану. Если «Сохранить» зелёное на форме входа, то и на странице настроек успешное действие тоже должно быть зелёным.
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); } /* равномерная для глаза модель */| Модель | Что задаёт | Когда удобна |
|---|---|---|
| hex | R, G, B в шестнадцатеричной форме | копирование из Figma, простая запись |
| rgb() / rgba() | R, G, B плюс прозрачность | работа с прозрачностью |
| hsl() | тон, насыщенность, светлота | перебор палитры: меняете только тон или светлость |
| oklch() / color() | новая модель с равномерным шагом | современные браузеры, широкий цветовой охват |
Почему hsl удобен для палитры? Потому что «тон» и «светлота» разделены. Зелёный светлее или темнее — это просто другая третья координата. В hex те же операции превращаются в непрозрачные коды вида #2563eb → #1e40af, в которых человеческий глаз не видит закономерности.
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); }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:
- 4.5:1 — обычный текст (уровень AA);
- 3:1 — крупный текст (от 24px или от 18.66px с полужирным) и границы интерактивных UI-компонентов;
- 7:1 — усиленный контраст для обычного текста (уровень AAA).
| Уровень | Обычный текст | Крупный текст | UI-границы |
|---|---|---|---|
| AA (обязателен) | 4.5:1 | 3:1 | 3:1 |
| AAA (желателен) | 7:1 | 4.5:1 | 3:1 |
Типовые ошибки: серый текст «для красоты» на белом фоне (контраст 2:1), тонкие линии ввода с едва заметной границей, светло-жёлтые уведомления с белым текстом.
6. Проверка контраста
Проверять контраст «на глаз» нельзя — человеческий глаз оценивает светлоту ненадёжно. Используйте инструменты, и они, во-первых, посчитают ratio точно, а во-вторых, избавят от споров в команде.
- Chrome DevTools — кликните по цвету в Styles и раскройте панель цветового круга: там сразу показывают ratio текущей пары и мини-кнопку выбора доступного оттенка;
- Lighthouse — аудит доступности отмечает элементы с контрастом ниже AA (курс Lighthouse);
- axe DevTools — расширение браузера, линтер доступности: находит слабый контраст и другие проблемы;
- онлайн-калькуляторы — например WebAIM Contrast Checker: вставляете два цвета, получаете ratio и вердикт по уровням AA/AAA.
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; }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 на фокусе. Цвет дополняет тактильные и пространственные подсказки, а не заменяет их.
Живой пример: состояния с дополнительными сигналами
Прогоните курсором по кнопкам, щёлкните и нажмите Tab с клавиатурой: каждое состояние меняет не только цвет, но и тень, сдвиг или контур.
9. Цвет слепоты: не только цвет
Дальтонизм (нарушение цветовосприятия) встречается примерно у 8% мужчин и 0.5% женщин. Самые частые формы — дейтераномалия и протанопия (сложности с красным и зелёным). Ровно те цвета, которыми помечают «успех» и «ошибку», люди с этими формами различают хуже всего.
Правило простое: рядом с цветом всегда должен быть второй сигнал, который не зависит от зрения.
/* Плохо: только цвет говорит об ошибке */
.valid { border-color: green; }
.invalid { border-color: red; }
/* Хорошо: цвет + текст + иконка */
.valid::after { content: "OK"; }
.invalid::after { content: "! Заполните поле"; }Какие сигналы дублируют цвет:
- текст — слово «Ошибка» рядом с красной рамкой;
- иконка — восклицательный знак, галочка, крест;
- полоса/форма — подчёркивание снизу, ярлык-бейдж, линия слева от блока;
- толщина — рамка ошибки толще обычной.
Пример валидации формы: вместо «поле просто стало красным» сделайте так, чтобы слева появился ярлык «Заполните поле» с иконкой и цветной рамкой. Тогда форма понятна и дальтонику, и человеку с монохромным экраном, и скринридеру.
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);
}Три типовые ошибки тёмных тем:
- чисто-чёрный фон (#000) ударяет по глазам и «растворяет» тени — берите тёмно-серый (#0f172a, #1e1e2e) и тени делайте светлее и прозрачнее;
- те же цвета текста — тёмный фон требует светлый текст, а muted-серый нужно осветлять, иначе контраст падает;
- «слепой» фон до загрузки — задайте цвет фона и
color-schemeв самом начале CSS, чтобы страница не вспыхивала белым при переключении.
: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 и — ключевой шаг — прогоняете все пары через проверку контраста. Генераторы не знают, на каком фоне будет ваш текст.
:root): преемник поймёт, откуда взялись цвета и почему они такие.12. Чек-лист перед сдачей
Финальная сверка перед тем, как показать страницу заказчику или коллеге. Прогоните пункты по порядку — они покрывают всё, о чём шла речь в уроке.
контраст ≥4.5:1для обычного текста и ≥3:1 для крупного и UI-границ — проверил инструментом, не глазом;все состояния различимы— hover, focus, active, disabled не теряют контраст и дают дополнительный сигнал;цвет не единственный сигнал— ошибка, успех и предупреждение дублированы текстом, иконкой или формой;палитра в переменных— роли записаны в:root, перекрашивание меняет одну строку;тёмная тема не ломает контраст— пары оттенков пересчитаны для тёмного фона, фон не чисто чёрный, цвет фона задан до загрузки;фокус виден— клавиатурная навигация немыслима без контрастного outline;checked у флажка и радио— состояние выбора явное и по цвету, и по форме иконки.
Что дальше
Ты разобрался с цветом, контрастом и доступностью — теперь интерфейс не только аккуратный, но и читаемый для всех. Вернись к базовым свойствам в курсе CSS, прокачай оформление текста в уроке Веб-типографика, а затем собери всё вместе в Практике: лендинг — там подручные палитры и состояния применяются к полноценной странице.