OKLCH и современные цвета CSS — почему ваша палитра выглядит неправильно в некоторых браузерах

Обновлено

RGB и HSL были разработаны для мониторов на катодных люминесцентных экранах. Вот почему OKLCH исправляет перцептуальную однородность, как цвет-микс() изменяет смешивание градиентов и что относительный синтаксис цвета открывает без предобработчика.

OKLCH и современные цвета CSS — Почему ваша палитра выглядит не так в некоторых браузерах 1
Реклама · УДАЛИТЬ?

Вы выбираете синий для основного кнопки. Он выглядит правильно в вашем браузере. Вы открываете стадию на новом MacBook Pro и он выглядит... нормально — просто немного плоским. Ваш дизайнер отправляет скриншот с iPhone, и та же кнопка выглядит значительно ярче. Тот же шестнадцатеричный код. Разные дисплеи.

Это разрыв P3. И на самом деле это наименее интересная часть того, почему цветовая модель CSS, которую вы используете, не срабатывает. Большая проблема — это математика в HSL.

Предел sRGB

Каждый шестнадцатеричный цвет и rgb() значение, которое вы когда-либо писали, живет в пространстве цветов sRGB — стандарте, введенном в 1996 году для мониторов с катушками. Пространство sRGB охватывает примерно 35% цветов, видимых человеческим глазом. Пространство P3, используемое на каждом iPhone с 2017 года и большинстве MacBooks с 2016 года, охватывает около 45%.

Когда вы пишете #3b82f6, этот цвет ограничивается sRGB. На дисплее P3 теоретически может быть отображен более насыщенный синий — но ваш CSS не просит этого. Вы оставляете возможности дисплея без использования по умолчанию.

Чтобы получить цвета P3 напрямую, вы должны использовать color(display-p3 0.2 0.4 0.9). Это работает. Но это не то, почему важно OKLCH. Большая неудача — в фундаментальном предположении HSL.

Светлота HSL — это ложь

HSL была реальным улучшением по сравнению с шестнадцатеричными значениями. Цвет, насыщенность, светлота — концептуально логичные, редактируемые человеком. Проблема: «L» не означает то, что вы думаете.

Поставьте жёлтый на hsl(60, 100%, 50%) и синий на hsl(240, 100%, 50%) рядом. Оба находятся ровно на 50% светлоты.

.yellow { background: hsl(60, 100%, 50%); }
.blue   { background: hsl(240, 100%, 50%); }

Жёлтый выглядит почти белым. Синий выглядит серым. Они не близки по восприятию яркости. Это неоднородность восприятия HSL — ось L не отражает то, как человеческое зрение воспринимает яркость.

Это создает реальную проблему при создании шкал цветов. Если вы генерируете 9-шаговую шкалу, шагая по значениям L в HSL, ваши жёлтые и синие цвета выглядят перегруженными, а ваши синие и фиолетовые — мутными. Вам приходится вручную корректировать значения, что полностью уничтожает смысл системного подхода.

OKLCH: разработан для восприятия

OKLCH был разработан Бьёрном Оттоссоном в 2020 году. Он основан на CIELAB (перцептуальном пространстве цветов из 1970-х), но исправляет известные сдвиги по оттенкам — особенно проблему с фиолетовым и синим, где изменение насыщенности вызывает визуальный сдвиг оттенка.

Три канала:

  • L (светлота) — от 0 до 1, перцептуально равномерный. L 0.5 в жёлтом выглядит так же ярко, как и L 0.5 в синем. На самом деле.
  • C (насыщенность) — начинается с 0 (серый), увеличивается до примерно 0.3–0.4 для сильно насыщенных цветов. Нет фиксированного максимума — верхний предел варьируется в зависимости от оттенка и целевого гамма.
  • Н (Цветовой тон) — от 0 до 360 градусов, примерно аналогично оттенку в HSL.

Вот Tailwind blue-500 в обоих системах:

/* sRGB hex */
background: #3b82f6;

/* OKLCH equivalent — same color, different notation */
background: oklch(0.623 0.214 259.1);

/* Push chroma past sRGB — more vivid blue on P3 displays, clips gracefully on sRGB */
background: oklch(0.623 0.27 259.1);

Последнее значение выходит за пределы гаммы sRGB. Браузеры на дисплеях с поддержкой P3 отображают более яркий вариант; старые браузеры ограничивают до ближайшего эквивалента в sRGB. Прогрессивное улучшение без усилий.

color-mix() и почему пространство цветов меняет смешивание

color-mix() была выпущена в Chrome 111, Firefox 113 и Safari 16.2. Она смешивает два цвета в определённом соотношении — достаточно просто. Но пространство цветов, которое вы указываете, значительно меняет результат.

/* Mixes through sRGB — midpoint is a washed-out brown-grey */
background: color-mix(in srgb, oklch(0.7 0.2 30), oklch(0.7 0.2 270));

/* Mixes through the OKLCH color wheel — midpoint is a vivid purple */
background: color-mix(in oklch, oklch(0.7 0.2 30), oklch(0.7 0.2 270));

Когда вы смешиваете оранжевый и синий в sRGB, вы усредняете значения каналов. Средняя точка проходит через десатурированный бурый. В OKLCH вы интерполируете по цветовому кругу — вы попадаете на яркий фиолетовый. То, что правильно, зависит от намерений, но для переходов в интерфейсе, градиентов и состояний hover OKLCH интерполяция почти всегда является желаемой.

Практическое применение: генерация состояния "отключено" без необходимости прописывать второй шестнадцатеричный код.

.button--disabled {
  background: color-mix(in oklch, var(--color-primary) 40%, white);
  cursor: not-allowed;
}

Синтаксис относительных цветов

Синтаксис относительных цветов (Chrome 119+, Firefox 128+, Safari 16.4+) позволяет производить новый цвет, изменяя отдельные каналы существующего:

:root {
  --brand: oklch(0.623 0.214 259.1);
}

.button:hover {
  /* Lighten by 8% lightness units */
  background: oklch(from var(--brand) calc(l + 0.08) c h);
}

.button:active {
  /* Darken and reduce chroma slightly */
  background: oklch(from var(--brand) calc(l - 0.08) calc(c - 0.02) h);
}

.button--muted {
  /* Drop chroma to near-zero: perceptual grey at the same lightness */
  background: oklch(from var(--brand) l 0.03 h);
}

Это заменяет Sass’ lighten(), darken()и desaturate() на встроенный CSS — без препроцессора, без этапа сборки, без необходимости синхронизировать параллельные шестнадцатеричные значения. И поскольку вы работаете в OKLCH, «осветление» действительно выглядит светлее для каждого оттенка в вашей палитре, а не только для некоторых из них.

Одна реальная ограниченность: сложные ограничения каналов (например, ограничение насыщенности для сохранения в гамме) требуют длинных цепочек calc() , которые быстро становятся сложными. Для простых изменений яркости и насыщенности синтаксис чистый. Для более сложных случаев более удобно вычислять значения на этапе сборки и генерировать CSS-свойства.

Поддержка браузеров в 2026 году

ОсобенностьChromeFirefoxSafari
oklch()111+113+15.4+
color-mix()111+113+16.2+
Синтаксис относительных цветов119+128+16.4+
color(display-p3)111+113+10+

Глобальная поддержка oklch() превышает 90% к середине 2026 года. Остаются старые браузеры на базе Chromium и любые версии Firefox ниже 113. Если они входят в вашу аудиторию, вам нужно всего лишь два строки фиксации:

.button {
  background: #3b82f6; /* sRGB fallback */
}

@supports (background: oklch(0 0 0)) {
  .button {
    background: oklch(0.623 0.214 259.1);
  }
}

На практике: если ваш анализ подтверждает Chrome 111+, Firefox 113+ и Safari 15.4+, вы можете полностью пропустить фиксацию. Упаковка @supports предназначена для спокойствия в внутренних инструментах, где вы не контролируете версии браузеров.

Перевод вашей существующей палитры

Точка сопротивления при внедрении OKLCH — переход от ваших текущих шестнадцатеричных значений к координатам OKLCH и понимание того, что означают каналы для каждого цвета. Утилита Конвертер универсальных цветовых пространств обрабатывает перевод в обе стороны — вставьте шестнадцатеричное или значение HSL, получите OKLCH, затем начните корректировать L и C с реальной точки отсчёта. Поддерживает HSL, RGB, Lab, LCH, HSV и OKLCH в одном месте, что полезно, когда вы работаете с файлом Figma, который предоставляет шестнадцатеричные значения, и хотите получить значения OKLCH, которые можно логически анализировать.

Как на самом деле перейти

Вы не обязаны переписывать всё сразу. Практическая последовательность:

  1. Начните с интерактивных цветов — ваши основные, вторичные и разрушительные токены. Эти цвета вы уже генерируете в состоянии hover, active и disabled. OKLCH и синтаксис относительных цветов немедленно заменяют эту логику на встроенном CSS.
  2. Оставьте статические цвета в шестнадцатеричном формате на данный момент — текст, фон, границы. Они не подвергаются программному изменению, поэтому пока нет выгоды от их преобразования.
  3. Создайте следующую систему с нуля в OKLCH — именно там, где преимущества перцептуальной равномерности становятся структурными. Шаг 500 вашей нейтральной шкалы действительно будет находиться в визуальном центре, а насыщенные оттенки будут одинаковыми по всем оттенкам без ручной настройки.

Цель не в том, чтобы использовать OKLCH ради этого. Цель — иметь систему цветов, в которой математика соответствует тому, что видит ваш глаз — так что вы перестанете компенсировать каждый раз, когда нужно чуть светлее синий.

Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?