Выбирайте OKLCH, когда его координаты соответствуют задаче
OKLCH — полярная форма Oklab. L описывает воспринимаемую светлоту, C — chroma, а H — угол цветового тона. CSS Color 4 называет Oklab пространством, оптимизированным по визуально похожим цветам, и отмечает более линейное поведение hue, лучшую перцептивную равномерность и оценку chroma по сравнению с CIE LCH. Поэтому OKLCH полезен для связанных токенов, шкал светлоты и интерполяции, которая реже проваливается в тусклый серый центр. Но модель лишь улучшает предсказуемость: каждый шаг всё равно нужно смотреть на реальных дисплеях.
- Используйте L для более светлых и тёмных ролей, помня, что равные числа лишь приблизительно соответствуют равным визуальным шагам.
- Используйте C как расстояние от нейтральной оси: рост значения усиливает цветность, пока ограничением не станет выбранный gamut.
- Используйте H для удержания одной цветовой семьи или явного выбора пути между hue при интерполяции.
- Не принимайте L, C и H за доказательство доступности. Контраст WCAG считается по относительной яркости итоговых отображаемых sRGB-цветов.
- Храните вместе с исходным OKLCH целевой gamut и fallback, чтобы токен воспроизводился одинаково в браузерах, дизайн-инструментах и документации.
Записывайте синтаксис без ловушки процентов chroma
Современная запись использует пробелы и необязательную косую черту перед alpha: oklch(L C H / A). Запятые недопустимы. Lightness задаётся числом от 0 до 1 или процентами от 0% до 100%. Chroma обычно удобнее записывать числом. По CSS Color 4 её 100% соответствуют 0.4, поэтому 25% означают 0.1, а не 0.25. Единого максимума C для всех цветов нет: практический диапазон доходит примерно до 0.5, а конкретная граница зависит от L, H и целевого gamut. Отрицательная chroma приводится к нулю.
:root {
--brand: oklch(70% 0.16 30);
--brand-same-chroma: oklch(70% 40% 30); /* 40% от 0.4 = 0.16 */
--brand-muted: oklch(70% 0.08 30);
--brand-overlay: oklch(70% 0.16 30 / 65%);
}| Координата | Запись CSS | Практический смысл | Важное ограничение |
|---|---|---|---|
| L | 70% или 0.7 | Координата воспринимаемой светлоты | Не равна относительной яркости WCAG |
| C | 0.16 или 40% | Расстояние от нейтральной оси | 100% соответствуют 0.4 |
| H | 30 или 30deg | Полярный угол hue | Не влияет при почти нулевой chroma |
| A | / 65% | Непрозрачность до композиции | Контраст зависит от фона |
Стройте шкалу светлоты и проверяйте каждый foreground
В этой шкале chroma зафиксирована на 0.08, hue — на 30, а L меняется от 35% до 85%. HEX — это 8-битные приближения sRGB, рассчитанные матрицами Oklab из CSS Color 4 и округлённые до ближайшего значения канала. После этого контраст заново рассчитан для каждого округлённого HEX по формуле относительной яркости WCAG. Уровень AA для обычного текста требует не менее 4,5:1. OKLCH L организует шкалу, но доказательство доступности даёт только итоговый скомпозированный цвет.
| OKLCH | 8-битный sRGB | Чёрный текст | Белый текст | Выбор AA для обычного текста |
|---|---|---|---|---|
| oklch(35% 0.08 30) | #5D2820 | 1,79:1 | 11,73:1 | Белый |
| oklch(45% 0.08 30) | #7C433A | 2,72:1 | 7,72:1 | Белый |
| oklch(55% 0.08 30) | #9B5F55 | 4,16:1 | 5,05:1 | Белый |
| oklch(65% 0.08 30) | #BB7D72 | 6,29:1 | 3,34:1 | Чёрный |
| oklch(75% 0.08 30) | #DC9B90 | 9,13:1 | 2,30:1 | Чёрный |
| oklch(85% 0.08 30) | #FEBBAF | 12,94:1 | 1,62:1 | Чёрный |
- В этой конкретной шкале foreground меняется между L 55% и L 65%; универсального порога OKLCH lightness нет.
- При L 55% чёрный не проходит AA для обычного текста, хотя шаг выглядит серединой шкалы: коэффициент равен 4,16:1.
- Сравнивайте с порогом неокруглённое значение, а округляйте только число в документации.
- Проверяйте hover, selected, disabled, градиентные и полупрозрачные состояния после композиции всех слоёв.
- Приближённый HEX полезен для проверки sRGB, но не способен точно сохранить исходный цвет за пределами sRGB.
Увеличивайте chroma до границы целевого gamut
У chroma нет одного безопасного максимума для всех палитр. При фиксированных L 70% и H 30 значения до C 0.19 в расчёте ниже остаются в sRGB. Уже C 0.20 выходит за sRGB, но ещё помещается в Display P3; C 0.24 тоже является корректным OKLCH-цветом внутри этого более широкого gamut. При других L или H граница окажется в другом месте. Для значений вне sRGB точного 8-битного HEX не существует: подстановка обрезанного HEX скрыла бы изменение gamut и могла бы сдвинуть hue или lightness.
| OKLCH при L 70%, H 30 | 8-битное приближение sRGB | sRGB | Display P3 | Интерпретация |
|---|---|---|---|---|
| C 0.00 | #9E9E9E | Внутри | Внутри | Нейтральный; hue не влияет |
| C 0.08 | #CB8C81 | Внутри | Внутри | Приглушённый цвет |
| C 0.16 | #F17260 | Внутри | Внутри | Выразительный цвет sRGB |
| C 0.19 | #FE6652 | Внутри | Внутри | Рядом с этой границей sRGB |
| C 0.20 | — | Снаружи | Внутри | Нет точного HEX |
| C 0.24 | — | Снаружи | Внутри | Широкий gamut |
Разделяйте fallback синтаксиса и gamut mapping
Fallback и gamut mapping решают разные задачи. Первый даёт старому ПО понятную декларацию. Поддерживающий OKLCH браузер способен разобрать запись, но всё равно может преобразовать цвет под gamut дисплея. Для custom property безопаснее использовать @supports: если записать неподдерживаемый цвет только в переменную, сама переменная останется допустимой до подстановки, а использующее её свойство может стать невалидным вместо возврата к предыдущему значению. Условный блок ниже исключает эту ловушку.
:root {
--brand: #FE6652;
}
@supports (color: oklch(70% 0.20 30)) {
:root {
--brand: oklch(70% 0.20 30);
}
}
.button {
color: #000000;
background: var(--brand);
}- Выбирайте fallback осознанно: #FE6652 здесь является последним шагом внутри sRGB, но не заявлен как точная копия C 0.20.
- Укажите, нацелен ли основной токен на sRGB, Display P3 или другой gamut, и проверяйте каждую ветку на подходящих дисплеях.
- Не обрезайте все токены заранее ради одинакового результата инструментов: так теряется широкогамутный источник и нарушается равномерность шкалы.
- Ожидайте gamut mapping браузера или цветового pipeline на выводе и проверяйте отображённый результат, а не предполагаемый алгоритм.
- Считайте контраст отдельно для fallback и mapped-версии, а не переносите один коэффициент с исходных координат.
Осознанно выбирайте пространство и путь интерполяции
Пространство между endpoint не менее важно, чем запись самих endpoint. CSS-градиенты и color-mix() позволяют запросить интерполяцию в OKLCH. Полярное пространство способно лучше сохранить chroma, чем смешивание прямоугольных каналов через тусклый центр, однако вокруг круга hue существует несколько маршрутов. Shorter и longer дают принципиально разные промежуточные семьи, а increasing и decreasing фиксируют направление. Даже при отображаемых endpoint промежуточный цвет может выйти за gamut, поэтому готовую шкалу всё равно проверяют на gamut и контраст.
.spectrum {
background: linear-gradient(
90deg in oklch shorter hue,
oklch(70% 0.16 30),
oklch(70% 0.16 280)
);
}
:root {
--brand-soft: color-mix(
in oklch,
oklch(70% 0.16 30) 35%,
white
);
}- Используйте Oklab для прямой перцептивной интерполяции, а OKLCH — когда важнее полярный путь hue или сохранение chroma.
- Указывайте shorter, longer, increasing или decreasing, если маршрут меняет смысл продукта; семантическая шкала не должна зависеть от случайного default.
- Проверяйте промежуточные остановки. Анализ только endpoint не выявит вышедший за gamut или низкоконтрастный центр.
- Смешивание с белым меняет не только L. Если должна изменяться одна координата, генерируйте явные OKLCH-токены.
- Используйте один документированный pipeline, чтобы дизайн-превью, CSS, скриншоты и аудиты применяли одинаковые правила интерполяции.
Публикуйте OKLCH-токены с проверяемыми данными
Рабочую OKLCH-палитру нужно уметь проверить без восстановления скрытых настроек дизайн-инструмента. Храните исходные координаты с достаточной точностью, называйте целевой gamut, добавляйте осознанный fallback и публикуйте рассчитанные данные рядом с семантическим токеном. Затем тестируйте фактически отображённые состояния. Так перцептивные преимущества модели остаются, а поддержка браузеров, gamut и доступность становятся обычными инженерными входными данными.
:root {
--action-strong: oklch(55% 0.08 30);
--action: oklch(65% 0.08 30);
--action-soft: oklch(85% 0.08 30);
--on-action-strong: #FFFFFF; /* 5,05:1 на #9B5F55 */
--on-action: #000000; /* 6,29:1 на #BB7D72 */
--on-action-soft: #000000; /* 12,94:1 на #FEBBAF */
}- Не смешивайте L с относительной яркостью WCAG, а C — с HSL saturation.
- Документируйте нормализацию процентов, особенно правило CSS: C 100% равна 0.4.
- Считайте hue бессильным у нейтральных цветов и не создавайте нестабильные названия по невидимой координате.
- Отмечайте токены внутри sRGB, токены для широкого gamut и fallback при отсутствии основного пути.
- Сохраняйте точность для преобразования, но называйте округлённый HEX именно 8-битным приближением sRGB.
- Проверяйте контраст итоговой композиции: alpha, градиенты, overlay и gamut mapping меняют увиденную пользователем пару.
- Отдельно проверяйте промежуточные цвета, focus indicator, border, hover, selected, светлую, тёмную и forced-colors темы.
- Ссылайтесь на спецификацию и метод преобразования, чтобы изменения генератора оставались объяснимыми и воспроизводимыми.
Первоисточники
Фактические утверждения статьи проверены по этим опубликованным спецификациям.
- W3C CSS Color 4 — обзор Oklab и OkLCh
- W3C CSS Color 4 — задание Oklab и OkLCh
- W3C CSS Color 4 — преобразование цветового охвата
- W3C CSS Color 4 — методы интерполяции цвета
- W3C CSS Images 4 — интерполяция цвета в градиентах
- W3C CSS Color 5 — color-mix()
- W3C CSS Conditional 3 — @supports
- W3C WCAG 2.2 — относительная яркость
- W3C WCAG 2.2 — контраст (минимальный)