Почему разработчики переходят с RGB на HSL для цветов интерфейса

Сравнение цветовых моделей RGB и HSL с оформлением редактора кода и образцами цветов

Цветовая модель HSL (тон, насыщенность, светлота) даёт разработчикам куда более понятный способ работы с цветом в CSS по сравнению с RGB. Вместо того чтобы угадывать, какое сочетание красного, зелёного и синего даст нужный оттенок, HSL позволяет описывать цвет так, как ты о нём думаешь: выбрать цвет, задать его яркость, а потом решить, насколько светлым или тёмным он должен быть. Именно этот сдвиг в мышлении объясняет, почему всё больше разработчиков отказываются от rgb() в пользу hsl() .

RGB vs HSL - Ключевое отличие

RGB задаёт цвет путём смешения трёх световых каналов: красного, зелёного и синего, каждый из которых принимает значения от 0 до 255. Проблема в том, что это способ отображения пикселей на экране, а не то, как человек воспринимает цвет. Хочешь сделать синий чуть светлее в RGB - придётся увеличить все три канала в точной пропорции. Никакого единственного параметра, который можно просто подкрутить, нет.

HSL напрямую отражает то, как люди описывают цвет в обычной речи:

  • Тон (Hue) - сам цвет, выраженный в градусах на цветовом круге из 360 градусов. Красный - 0, зелёный - 120, синий - 240.
  • Насыщенность (Saturation) - насколько цвет яркий или блёклый: от 0% (чистый серый) до 100% (максимально насыщенный).
  • Светлота (Lightness) - насколько цвет светлый или тёмный: от 0% (чёрный) до 100% (белый), при этом 50% - «чистый» цвет.

Вот один и тот же средне-синий цвет в обоих форматах:

/* RGB - what do these numbers even mean at a glance? */
color: rgb(70, 130, 180);

/* HSL - instantly readable: blue hue, moderate saturation, medium lightness */
color: hsl(207, 44%, 49%);

Оба варианта дают стально-синий цвет. Но только HSL-версия говорит тебе что-то полезное с первого взгляда. Тон - 207 (синеватый), насыщенность умеренная - 44%, светлота посередине - 49%. Можно рассуждать о цвете без открытого палитрового инструмента.

Свойство RGB HSL
Читаемость для человека Редко Да
Осветлить/затемнить цвет Менять 3 значения Менять только светлоту
Изменить насыщенность Пересчитывать все 3 Менять только насыщенность
Создать цветовую палитру Методом проб и ошибок Поворот тона на градусы
Темизация с CSS-переменными Громоздко Чисто и предсказуемо

Синтаксис HSL в CSS

CSS-функция hsl() поддерживается начиная с CSS3. Современный синтаксис (CSS Color Level 4) также позволяет передавать четвёртый параметр прозрачности прямо внутрь hsl() , что делает hsla() в большинстве случаев избыточным:

/* Classic syntax */
color: hsl(207, 44%, 49%);

/* With alpha (transparency) - old way */
color: hsla(207, 44%, 49%, 0.8);

/* Modern CSS Color Level 4 syntax - commas optional, alpha with slash */
color: hsl(207 44% 49%);
color: hsl(207 44% 49% / 0.8);
color: hsl(207 44% 49% / 80%);

Все основные браузеры поддерживают оба синтаксиса. Синтаксис без запятых - это направление, в котором движется CSS, но вариант с запятыми работает везде, включая Internet Explorer 9+.

Почему HSL выигрывает в UI-разработке

Настоящая сила HSL проявляется в тот момент, когда нужно генерировать вариации цвета - а в UI-разработке это происходит постоянно.

Создание цветовой палитры за считанные секунды

Допустим, фирменный цвет твоего проекта - насыщенный зелёный hsl(140, 70%, 45%) . Тебе нужны состояние при наведении, отключённое состояние и светлый фоновый оттенок. В HSL каждый раз меняется только одно значение:

--color-base:     hsl(140, 70%, 45%);  /* base green */
--color-hover:    hsl(140, 70%, 38%);  /* darker - just lower lightness */
--color-disabled: hsl(140, 20%, 65%);  /* washed out - lower saturation */
--color-tint:     hsl(140, 70%, 92%);  /* very light background tint */

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

Аналогичные и дополнительные цвета

Поскольку тон - это градус на цветовом круге, создание гармоничных палитр превращается в арифметику. Аналогичные цвета находятся в пределах 30 градусов друг от друга. Дополнительные (комплементарные) цвета отстоят на 180 градусов:

--primary:       hsl(210, 80%, 50%);   /* blue */
--analogous-1:   hsl(180, 80%, 50%);   /* cyan - 30 degrees left */
--analogous-2:   hsl(240, 80%, 50%);   /* purple - 30 degrees right */
--complementary: hsl(30, 80%, 50%);    /* orange - 180 degrees opposite */

Насыщенность и светлота остаются одинаковыми, поэтому цвета воспринимаются как единое семейство. Именно так дизайн-системы вроде Tailwind CSS и Material Design программно генерируют свои цветовые шкалы.

Практические примеры CSS

Кастомные CSS-свойства с компонентами HSL

Один из самых мощных паттернов с HSL - разбить три значения на отдельные CSS-кастомные свойства. Это позволяет собирать цвет в любом месте и менять отдельные каналы на лету:

:root {
  --brand-h: 210;
  --brand-s: 80%;
  --brand-l: 50%;
  --brand-color: hsl(var(--brand-h), var(--brand-s), var(--brand-l));
}

.button {
  background-color: var(--brand-color);
}

.button:hover {
  /* Just override lightness - no need to redefine the whole color */
  background-color: hsl(var(--brand-h), var(--brand-s), 40%);
}

.button:disabled {
  background-color: hsl(var(--brand-h), 20%, 70%);
}
Примечание о синтаксисе: При использовании CSS-кастомных свойств внутри hsl() нужны запятые: hsl(var(--h), var(--s), var(--l)) . Современный синтаксис без запятых пока не работает с var() внутри hsl() во всех браузерах.

Генерация полной тональной шкалы

Дизайн-системы часто требуют 9-10 оттенков одного цвета (как шкала Tailwind от 50 до 950). С HSL можно сгенерировать всю шкалу, просто увеличивая светлоту с равным шагом, сохраняя тон и насыщенность неизменными:

:root {
  --blue-50:  hsl(210, 80%, 95%);
  --blue-100: hsl(210, 80%, 87%);
  --blue-200: hsl(210, 80%, 76%);
  --blue-300: hsl(210, 80%, 65%);
  --blue-400: hsl(210, 80%, 55%);
  --blue-500: hsl(210, 80%, 50%);  /* base */
  --blue-600: hsl(210, 80%, 43%);
  --blue-700: hsl(210, 80%, 36%);
  --blue-800: hsl(210, 80%, 26%);
  --blue-900: hsl(210, 80%, 16%);
}

Если хочешь подробнее разобраться в том, как форматы цвета соотносятся друг с другом, в руководстве по конвертации HEX в RGB рассмотрена полная картина форматов цвета в CSS, включая ситуации, когда каждый из них является правильным инструментом.

Состояния hover, темизация и тёмная тема

Состояния hover и focus

HSL делает состояния при наведении тривиальными. Вместо того чтобы задавать совершенно отдельный цвет, достаточно просто сдвинуть светлоту:

.btn-primary {
  background: hsl(210, 80%, 50%);
  transition: background 0.2s ease;
}

.btn-primary:hover  { background: hsl(210, 80%, 43%); }
.btn-primary:active { background: hsl(210, 80%, 36%); }
.btn-primary:focus-visible {
  outline: 3px solid hsl(210, 80%, 70%);
}

Каждое состояние явно связано с базовым цветом. Коллега, читающий этот код, сразу понимает взаимосвязь между состояниями.

Темизация с HSL-переменными

HSL - это основа современной темизации в CSS. Вынося только тон в переменную, ты позволяешь пользователям или администраторам менять всю цветовую схему приложения, изменив одно число:

/* Default theme: blue */
:root {
  --theme-hue: 210;
}

/* Green theme - just swap the hue */
[data-theme="green"] {
  --theme-hue: 140;
}

/* Purple theme */
[data-theme="purple"] {
  --theme-hue: 270;
}

/* All components use the same hue variable */
.button      { background: hsl(var(--theme-hue), 75%, 50%); }
.link        { color: hsl(var(--theme-hue), 75%, 40%); }
.badge       { background: hsl(var(--theme-hue), 75%, 92%); color: hsl(var(--theme-hue), 75%, 25%); }
.focus-ring  { outline-color: hsl(var(--theme-hue), 75%, 65%); }

Тёмная тема с HSL

Тёмная тема - это именно то место, где HSL по-настоящему вырывается вперёд. Вместо того чтобы поддерживать две полностью отдельные цветовые палитры, ты просто инвертируешь значения светлоты в рамках того же тона:

:root {
  --bg:   hsl(210, 20%, 98%);   /* near-white background */
  --text: hsl(210, 20%, 15%);   /* near-black text */
  --card: hsl(210, 20%, 93%);   /* slightly darker card */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:   hsl(210, 20%, 10%);  /* flip: near-black background */
    --text: hsl(210, 20%, 90%);  /* flip: near-white text */
    --card: hsl(210, 20%, 15%);  /* slightly lighter card in dark */
  }
}

Обрати внимание: тон (210) и насыщенность (20%) не меняются никогда. Инвертируется только светлота. Это позволяет тёмной теме ощущаться как то же самое оформление, просто перевёрнутое - а именно так и должна работать хорошая тёмная тема.

Поддержка браузеров и совместимость

HSL отлично поддерживается браузерами. Функция hsl() доступна начиная с:

  • Chrome 1 (2008)
  • Firefox 1 (2004)
  • Safari 3.1 (2008)
  • Internet Explorer 9 (2011)
  • Edge 12 (2015)

Современный синтаксис без запятых ( hsl(210 80% 50%) ) и синтаксис с прозрачностью через слэш ( hsl(210 80% 50% / 0.5) ) входят в спецификацию CSS Color Level 4 и поддерживаются во всех современных браузерах начиная с 2023 года. Если тебе нужна поддержка IE11 (что сегодня редкость), придерживайся синтаксиса с запятыми.

Можно использовать уже сейчас: hsl() с запятыми работает в каждом браузере, которым пользуются твои пользователи. Современный синтаксис с пробелами вместо запятых поддерживается в Chrome 90+, Firefox 89+ и Safari 14.1+, что покрывает более 95% мирового использования браузеров.

Ты всегда можешь воспользоваться палитровым инструментом , чтобы конвертировать значения между HSL, RGB и HEX - когда нужно сверить значения или передать цвета в инструменты, принимающие только один формат.

HSL vs OKLCH - Что дальше?

Если хочешь пойти глубже HSL, стоит познакомиться с OKLCH . Это перцептивно равномерное цветовое пространство: одинаковые числовые шаги по светлоте или насыщенности действительно воспринимаются человеческим глазом как одинаковые - чего HSL полностью гарантировать не может.

Проблема HSL в том, что два цвета с одинаковым значением светлоты могут выглядеть совершенно по-разному по воспринимаемой яркости. Например, hsl(60, 100%, 50%) (жёлтый) выглядит значительно ярче, чем hsl(240, 100%, 50%) (синий), хотя у обоих светлота 50%. OKLCH исправляет этот недостаток.

/* HSL - same lightness, different perceived brightness */
color: hsl(60, 100%, 50%);   /* yellow - looks very bright */
color: hsl(240, 100%, 50%);  /* blue - looks much darker */

/* OKLCH - same lightness, actually looks the same to the eye */
color: oklch(0.75 0.18 90);   /* yellow-ish */
color: oklch(0.75 0.18 260);  /* blue-ish - genuinely similar perceived brightness */

OKLCH также поддерживает цвета широкой гаммы (P3 display colors), выходящие за пределы диапазона sRGB, которым ограничен HSL. Поддержка oklch() в браузерах уверенная в 2024 году: Chrome 111+, Firefox 113+, Safari 15.4+.

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

Если ты исследуешь цветовые взаимосвязи визуально в процессе создания палитры, инструмент цветового исследования позволяет экспериментировать со значениями HSL и в реальном времени видеть, как взаимодействуют повороты тона, сдвиги насыщенности и изменения светлоты.

Инструмент выбора цвета HSL со слайдерами тона, насыщенности и светлоты

Подбирай HSL-цвета без лишних догадок

Наш палитровый инструмент позволяет визуально настраивать тон, насыщенность и светлоту, а затем скопировать готовый HSL-цвет прямо в CSS - никаких вычислений в уме.

Попробовать палитровый инструмент →

Полный рефакторинг не обязателен, но стоит переходить на HSL для любых новых определений цвета, особенно при использовании CSS-кастомных свойств. Наибольший выигрыш ты получишь при построении или поддержке дизайн-системы, создании состояний при наведении или реализации тёмной темы. Смешивать RGB и HSL в одном проекте - совершенно валидный CSS: браузеры обрабатывают оба формата без какой-либо разницы в производительности.

Нет, никакой значимой разницы в производительности нет. Браузер конвертирует все форматы цвета CSS во внутреннее представление на этапе парсинга, поэтому независимо от того, напишешь ли ты hsl(207, 44%, 49%) или rgb(70, 130, 180) , движок рендеринга после этого шага обрабатывает их одинаково. Выбор формата - исключительно вопрос удобства разработки и удобства поддержки, а не производительности во время выполнения.

Да, и это один из сильнейших сценариев применения HSL в JavaScript. Можно хранить тон, насыщенность и светлоту как отдельные числа, а затем динамически собирать строку цвета: element.style.color = `hsl(${hue}, ${saturation}%, ${lightness}%)` . Это делает анимации, переключение тем и интерактивные элементы управления цветом значительно проще в реализации, чем работа с RGB-каналами, где для достижения тех же эффектов потребовалась бы математика конвертации.

HSL (тон, насыщенность, светлота) и HSB/HSV (тон, насыщенность, яркость/значение) - это разные цветовые модели, хотя у них общая ось тона. В HSL светлота 50% даёт чистый, полностью насыщенный цвет. В HSB яркость 100% даёт чистый цвет. При одинаковых значениях насыщенности и яркости/светлоты эти модели дают разные результаты. CSS использует именно HSL - HSB/HSV распространён в инструментах дизайна вроде Photoshop и Figma, но не является нативным форматом CSS.

Конвертация из HEX в HSL проходит через промежуточный шаг RGB: сначала HEX-пары переводятся в значения RGB (0-255), нормализуются до диапазона 0-1, а затем применяется формула конвертации в HSL. На практике большинство разработчиков используют палитровый инструмент, инструмент дизайна вроде Figma или встроенный палитровый инструмент DevTools браузера - это мгновенно, без ручных вычислений. DevTools браузера позволяет кликнуть на любой цветовой образец и переключаться между HEX, RGB и HSL-представлениями.

Да, OKLCH надёжнее для работы с доступностью, поскольку является перцептивно равномерным - одинаковые шаги по каналу светлоты соответствуют одинаковым изменениям воспринимаемой яркости. Это упрощает создание цветовых палитр с предсказуемыми коэффициентами контрастности для разных тонов. В HSL жёлтый с 50% светлоты выглядит значительно ярче, чем синий с 50% светлоты, что может приводить к неожиданным проблемам с доступностью. Для соответствия требованиям контрастности WCAG OKLCH даёт более предсказуемые результаты при создании доступных цветовых шкал.