Интерполяция цвета в CSS: почему побеждает OKLCH
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://colorfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://colorfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://colorfyi.com/entity//)
Use the native HTML custom element.
Каждый раз, когда CSS смешивает два цвета — в градиенте, в color-mix() или в переходе — выполняется интерполяция цвета: вычисление промежуточных значений между начальным и конечным цветом. На протяжении большей части истории веба это вычисление происходило в цветовом пространстве sRGB, и результаты нередко оказывались мутными, ненасыщенными или перцептивно неравномерными. В современном CSS можно выбирать цветовое пространство для интерполяции. Выбор имеет огромное значение, и OKLCH неизменно даёт наилучшие результаты.
В этом руководстве объясняется, почему интерполяция RGB даёт сбои, где не справляется HSL и почему OKLCH является правильным выбором по умолчанию для градиентов и смешения цветов в 2024 году и далее.
Проблема интерполяции RGB: мутные средние точки
Когда вы пишете CSS-градиент без указания цветового пространства, браузер интерполирует в sRGB — вычисляет промежуточные цвета, линейно смешивая значения каналов R, G и B по отдельности.
Это кажется разумным, но даёт серьёзный сбой, когда две конечные точки проходят через центр куба sRGB. Рассмотрим градиент от красного к зелёному:
/* По умолчанию: интерполяция в sRGB */
background: linear-gradient(to right, #FF0000, #00FF00);
Средняя точка этого градиента в sRGB — #808000 — тусклая олива. Математически RGB 128, 128, 0 — точно посередине между красным и зелёным по значениям каналов. Но перцептивно это совершенно не похоже на яркую среднюю точку между двумя насыщенными первичными цветами. Градиент проседает в мутную тёмную впадину, прежде чем посветлеть с другой стороны.
Фундаментальная проблема в том, что sRGB перцептивно не равномерен. Равные числовые шаги в R, G и B не соответствуют равным шагам воспринимаемого цвета или яркости. Куб sRGB был разработан для аппаратного обеспечения дисплеев, а не для человеческого зрения.
/* Можно явно задать sRGB */
background: linear-gradient(in srgb, red, blue);
/* Или использовать устаревший синтаксис (тот же результат) */
background: linear-gradient(red, blue);
Оба варианта дают одинаковые ненасыщенные средние точки. Проблема в самом цветовом пространстве, а не в синтаксисе.
Проблема гаммы
Здесь есть ещё одна тонкость: значения sRGB кодированы гаммой. Сырые значения sRGB 0,5 не соответствуют 50% физического света, излучаемого дисплеем. Фактическую перцептивную среднюю точку градиента нужно вычислять в линейном свете, а не в гамма-кодированном sRGB. CSS уровня 4 ввёл in srgb-linear для решения этой проблемы:
background: linear-gradient(in srgb-linear, red, blue);
Линейный sRGB лучше гамма-кодированного sRGB для переходов яркости, но всё ещё не решает проблемы сдвига оттенка и падения насыщенности, поскольку остаётся прямоугольным пространством RGB.
Сдвиги оттенка в HSL
HSL был представлен как более удобная для восприятия альтернатива RGB. Его цилиндрическое представление — Оттенок (Hue) в диапазоне 0–360 градусов, Насыщенность (Saturation) и Светлота (Lightness) в процентах — облегчает рассуждение о цветовых соотношениях. Но интерполяция HSL имеет собственный характерный сбой: сдвиги оттенка.
При интерполяции между двумя HSL-цветами браузер движется по кратчайшей дуге цветового круга. Если идти от тёплого красного (hsl(10, 100%, 50%)) к холодному синему (hsl(220, 100%, 50%)), кратчайший путь проходит через пурпурный и фиолетовый — что может быть желательным или нет.
Но более глубокая проблема в том, что Светлота HSL не является перцептивно равномерной. Жёлтый при hsl(60, 100%, 50%) выглядит значительно ярче синего при hsl(240, 100%, 50%), хотя они разделяют одно значение L. При интерполяции от жёлтого к синему в HSL воспринимаемая яркость резко прыгает и проседает несвязным образом.
/* Градиент HSL — перцептивная яркость неравномерна по оттенкам */
background: linear-gradient(in hsl, hsl(60 100% 50%), hsl(240 100% 50%));
Средняя точка этого градиента при hsl(150, 100%, 50%) — насыщенный зелёный, который воспринимается перцептивно ярче обеих конечных точек. Градиент достигает пика в середине вместо плавного перехода.
Интерполяция HSL также имеет проблему длинной дуги: угол оттенка оборачивается на 360°, и при интерполяции от hsl(350, ...) (почти красный) к hsl(20, ...) (оранжевый) HSL может пройти длинный путь через синий, зелёный и жёлтый вместо короткого через красный.
OKLCH: последовательность и перцептивная равномерность
OKLCH — цилиндрическая форма цветового пространства Oklab, разработанная Бьорном Оттоссоном в 2020 году специально для устранения недостатков перцептивной равномерности более ранних цветовых пространств. Его три канала — L (светлота), C (насыщенность) и H (угол оттенка) — откалиброваны так, что равные числовые изменения в L дают равные изменения воспринимаемой яркости независимо от оттенка.
При интерполяции между двумя OKLCH-цветами вы получаете:
- Последовательную воспринимаемую яркость на протяжении всего перехода — без проседания в тёмные средние точки и ярких пиков.
- Сохранённую насыщенность — средние точки остаются яркими, потому что насыщенность интерполируется независимо от оттенка.
- Более короткие, интуитивные пути оттенка — углы оттенков OKLCH распределены в перцептивном порядке, поэтому интерполяция проходит через ожидаемые промежуточные оттенки.
/* Градиент OKLCH — яркий, перцептивно равномерный */
background: linear-gradient(in oklch, red, blue);
Градиент от красного к синему в OKLCH проходит через яркий пурпурный и фиолетовый — перцептивно интуитивные средние точки. Он остаётся насыщенным и равномерно ярким на протяжении всего пути.
Сравнение рядом
| Градиент | Средний цвет | Воспринимаемое качество |
|---|---|---|
in srgb от красного к зелёному |
#808000 — тусклая олива | Мутная, тёмная впадина |
in hsl от жёлтого к синему |
Яркий зелёный пик | Неравномерная яркость, цветовой скачок |
in oklch от красного к зелёному |
Яркий тёплый жёлтый | Плавный, насыщенный, равномерный |
Чтобы увидеть разницу самостоятельно, используйте Генератор градиентов, который позволяет просматривать градиенты в разных цветовых пространствах.
Как конвертировать цвета в OKLCH
Не нужно переписывать каждое значение цвета в CSS. Флаг in oklch в градиенте предписывает браузеру конвертировать конечные точки в OKLCH перед интерполяцией, затем преобразовать обратно в выходное цветовое пространство. Можно оставлять значения HEX:
/* Эти конечные точки HEX автоматически конвертируются в OKLCH для интерполяции */
background: linear-gradient(in oklch, #FF5733, #3498DB);
#FF5733 примерно соответствует oklch(0.63 0.24 27), а #3498DB — примерно oklch(0.63 0.14 232). Используйте Конвертер цветов для проверки OKLCH-значений любого HEX-кода перед разработкой градиента, чтобы заранее знать, через что пройдёт интерполяция.
Функция color-mix()
CSS color-mix() — декларативный API для смешения двух цветов, доступный во всех современных браузерах с 2023 года. Его синтаксис включает цветовое пространство интерполяции в качестве первого аргумента:
color: color-mix(in oklch, #FF5733 50%, #3498DB);
Это смешивает #FF5733 и #3498DB в равных пропорциях в пространстве OKLCH. Результат — яркий промежуточный цвет, похожий на настоящую перцептивную среднюю точку между двумя цветами, а не мутное среднее sRGB.
Практические применения color-mix()
Создание прозрачных вариантов:
:root {
--brand: #2563EB;
--brand-10: color-mix(in oklch, var(--brand) 10%, transparent);
--brand-20: color-mix(in oklch, var(--brand) 20%, transparent);
--brand-50: color-mix(in oklch, var(--brand) 50%, transparent);
}
Тонирование в сторону белого или чёрного:
:root {
--brand: #2563EB;
--brand-light: color-mix(in oklch, var(--brand) 70%, white);
--brand-dark: color-mix(in oklch, var(--brand) 70%, black);
}
Это та же идея, что и генератор оттенков, но полностью в CSS, без препроцессинга. Результат тонирования в OKLCH выглядит более естественным, чем эквивалентные операции в HSL, потому что насыщенность соответственно смещается при приближении к белому или чёрному.
Вывод состояния при наведении:
.button {
background: var(--brand);
}
.button:hover {
background: color-mix(in oklch, var(--brand) 80%, black);
}
Предупреждение: color-mix() в sRGB
Если опустить аргумент in <colorspace>, color-mix() по умолчанию использует интерполяцию sRGB:
/* Это использует sRGB — может дать мутные результаты */
color: color-mix(#FF5733, #3498DB);
/* Правильно: явно указывайте пространство */
color: color-mix(in oklch, #FF5733, #3498DB);
Всегда явно указывайте in oklch (или другое предпочтительное пространство). Значение sRGB по умолчанию сохранено для обратной совместимости, но редко является лучшим выбором.
Цветовое пространство интерполяции в градиентах
Современный синтаксис CSS позволяет указывать цветовое пространство интерполяции непосредственно в объявлении градиента, используя in <colorspace> после ключевого слова направления:
/* sRGB (по умолчанию, часто мутный) */
background: linear-gradient(to right, red, blue);
/* Линейный sRGB (лучше по яркости, всё ещё с проблемами оттенка) */
background: linear-gradient(in srgb-linear to right, red, blue);
/* HSL (сдвиги оттенка, неравномерная яркость) */
background: linear-gradient(in hsl to right, red, blue);
/* OKLCH (рекомендуется: яркий, перцептивно равномерный) */
background: linear-gradient(in oklch to right, red, blue);
/* Oklab (тоже отличный, менее интуитивный синтаксис) */
background: linear-gradient(in oklab to right, red, blue);
Этот синтаксис применяется ко всем типам градиентов: linear-gradient, radial-gradient и conic-gradient.
Направление интерполяции оттенка в OKLCH
Для интерполяции оттенка можно управлять тем, идёт ли градиент по более короткой или более длинной дуге вокруг круга оттенков:
/* Кратчайшая дуга (по умолчанию) */
background: linear-gradient(in oklch, hsl(30 100% 50%), hsl(270 100% 50%));
/* Длинная дуга — проходит через жёлтый, зелёный, голубой, затем к фиолетовому */
background: linear-gradient(in oklch longer hue, hsl(30 100% 50%), hsl(270 100% 50%));
/* Возрастающая дуга — всегда движется в направлении увеличения угла оттенка */
background: linear-gradient(in oklch increasing hue, hsl(30 100% 50%), hsl(270 100% 50%));
Ключевое слово shorter hue (по умолчанию) даёт наиболее естественный результат в большинстве случаев. Вариант longer hue полезен для радужных эффектов или когда нужно, чтобы градиент проходил через широкую часть спектра оттенков.
Многоостановочные градиенты
Интерполяция OKLCH становится ещё ценнее для многоостановочных градиентов, поскольку каждый сегмент интерполирует через перцептивно яркие средние точки:
/* Радужный градиент, остающийся ярким на протяжении всего пути */
background: linear-gradient(
in oklch to right,
oklch(0.70 0.20 30), /* оранжево-красный */
oklch(0.80 0.20 90), /* жёлтый */
oklch(0.75 0.20 150), /* зелёный */
oklch(0.65 0.20 240), /* синий */
oklch(0.60 0.20 300) /* фиолетовый */
);
Поскольку значения L и C перцептивно откалиброваны, установка похожих значений для всех остановок даёт радугу, которая выглядит естественно яркой и насыщенной, без провалов яркости, характерных для sRGB-радуг.
Поддержка браузерами
Синтаксис in <colorspace> для градиентов поддерживается всеми основными браузерами:
- Chrome/Edge: Полная поддержка начиная с версии 111 (март 2023)
- Firefox: Полная поддержка начиная с версии 113 (май 2023)
- Safari: Полная поддержка начиная с версии 16.2 (декабрь 2022)
Для старых браузеров предусматривайте запасной вариант с устаревшим синтаксисом:
/* Запасной вариант: градиент sRGB */
background: linear-gradient(to right, #FF5733, #3498DB);
/* Современный: интерполяция OKLCH */
@supports (background: linear-gradient(in oklch, red, blue)) {
background: linear-gradient(in oklch to right, #FF5733, #3498DB);
}
Ключевые выводы
- Интерполяция sRGB (значение CSS по умолчанию) производит мутные, ненасыщенные средние точки, когда градиенты проходят вблизи центра куба sRGB — особенно заметно в градиентах от красного к зелёному и в дополняющих цветовых парах.
- Интерполяция HSL позволяет избежать некоторых проблем RGB, но вводит непоследовательную воспринимаемую яркость — её канал Светлоты не является перцептивно равномерным — и может производить неожиданные пути оттенков.
- Интерполяция OKLCH производит яркие, перцептивно последовательные средние точки, поскольку разработана с учётом человеческого зрения. Градиенты в OKLCH остаются насыщенными и равномерно яркими по всему диапазону.
- Используйте
in oklchв любом градиенте:linear-gradient(in oklch to right, red, blue). Используйтеcolor-mix(in oklch, color1 50%, color2)для программного смешения. - Конвертер цветов позволяет конвертировать значения HEX или RGB в OKLCH, чтобы понять, как ваши цвета будут интерполироваться до создания дизайна градиента.
- Генератор градиентов позволяет просматривать, как разные цветовые пространства влияют на вывод градиента для любой пары цветов.
- Направление интерполяции оттенка можно управлять с помощью ключевых слов
shorter hue,longer hue,increasing hueиdecreasing hue— полезно для радужных эффектов и точного управления многоостановочными градиентами.