01

Считайте HSL моделью редактирования sRGB

CSS Color 4 определяет hsl() как способ задать цвет sRGB через hue, saturation и lightness. Это не расширенный цветовой охват и не перцептивно равномерное пространство. Браузер преобразует координаты в красный, зелёный и синий каналы. Поэтому HSL удобен для ручной настройки и совместим с обычными HEX и rgb(), но итог следует проверять по разрешённым каналам sRGB. Именно они нужны для сравнения, хранения и расчёта контраста.

  • Hue — угол вокруг цилиндра HSL: 0deg соответствует красному, 120deg — зелёной первичной sRGB, 240deg — синей первичной sRGB.
  • Saturation 0% даёт ахроматический результат, а 100% — максимальную насыщенность HSL при выбранных hue и lightness.
  • Lightness 0% превращается в чёрный, 100% — в белый, а 50% является серединой модели, но не гарантией средней воспринимаемой яркости.
  • Alpha — отдельный компонент. Он меняет композицию с фоном, но не сохранённые координаты hue, saturation и lightness.
  • HEX, rgb() и hsl() способны описывать один и тот же цвет в пределах sRGB, хотя предлагают разные способы редактирования.
02

Записывайте синтаксис без двусмысленности

Современный CSS разделяет компоненты HSL пробелами, а alpha помещает после косой черты. Старый вариант с запятыми поддерживается ради совместимости, однако смешивать две грамматики нельзя. Hue принимает угол; для документации токенов явный deg обычно понятнее. Углы циклические: 0deg, 360deg и 720deg обозначают один hue. Если alpha не указан, цвет полностью непрозрачен.

css
:root {
  --accent: hsl(210deg 100% 40%);
  --accent-translucent: hsl(210deg 100% 40% / 60%);
}

/* Допустимый legacy-синтаксис для совместимости. */
.legacy-example {
  color: hsl(210, 100%, 40%);
}
Запись HSLИтоговый HEXЧто показывает пример
hsl(0 100% 50%)#FF00000deg — красный
hsl(360 100% 50%)#FF0000Hue зацикливается
hsl(120 100% 50%)#00FF00Зелёная первичная sRGB
hsl(240 100% 50%)#0000FFСиняя первичная sRGB
hsl(210 0% 50%)#808080Hue не влияет на ахроматический результат
03

Насыщенность меняет цветность, а не прозрачность

Зафиксируем hue на 210deg и lightness на 50%, после чего уменьшим saturation с шагом 25 пунктов. Цвет приближается к ахроматической оси, но остаётся полностью непрозрачным. Это не то же самое, что alpha: полупрозрачный синий показывает неизвестный фон, а сниженная HSL-насыщенность уже даёт новый непрозрачный триплет sRGB. Таблица рассчитана алгоритмом CSS Color 4; итоговые 8-битные каналы округлены до ближайшего целого.

НасыщенностьHSLИтоговый HEXИтоговый RGB
100%hsl(210 100% 50%)#0080FF0 128 255
75%hsl(210 75% 50%)#2080DF32 128 223
50%hsl(210 50% 50%)#4080BF64 128 191
25%hsl(210 25% 50%)#60809F96 128 159
0%hsl(210 0% 50%)#808080128 128 128
04

Шкала lightness может потребовать смены foreground

Теперь оставим hue 210deg и saturation 100%, меняя только HSL lightness. Последовательность генерируется просто, но один цвет текста подходит не всем шагам. Контраст вычислен для округлённого HEX по формуле относительной яркости sRGB из WCAG. Для обычного текста уровень AA требует не менее 4,5:1. Выбор чёрного или белого ниже означает только проверенную пару, а не сертификацию всей страницы.

LightnessИтоговый HEXЧёрный текстБелый текстВыбор AA для обычного текста
20%#0033661,67:112,61:1Белый
30%#004D992,52:18,33:1Белый
40%#0066CC3,77:15,57:1Белый
50%#0080FF5,53:13,80:1Чёрный
60%#3399FF7,14:12,94:1Чёрный
70%#66B3FF9,46:12,22:1Чёрный
80%#99CCFF12,44:11,69:1Чёрный
  • В этой конкретной шкале foreground меняется между lightness 40% и 50%.
  • Hover или selected с тем же hue всё равно требуют отдельных проверок текстового и нетекстового контраста.
  • Не округляйте коэффициент до сравнения с 4,5:1; округлять можно только показанное пользователю значение.
  • При наличии alpha, градиента, изображения или overlay сначала определите итоговые отображаемые цвета.
05

HSL lightness не равна воспринимаемой светлоте

Название lightness звучит как характеристика восприятия, но в HSL она выводится из крайних гамма-кодированных каналов sRGB. CSS Color 4 прямо предупреждает: эта координата не передаёт визуальную светлоту одинаково для разных hue. Насыщенные жёлтый и синий имеют HSL lightness 50%, однако их относительная яркость WCAG и подходящие foreground различаются радикально. Равные повороты hue тоже выглядят неравномерно: одни участки круга визуально сжаты, другие растянуты.

ЦветHSLHEXОтносительная яркостьЧёрный текстБелый текст
Жёлтыйhsl(60 100% 50%)#FFFF0092,78%19,56:11,07:1
Синийhsl(240 100% 50%)#0000FF7,22%2,44:18,59:1
06

Избегайте шести частых ошибок HSL

  • Не считайте HSL широкогамутным форматом: CSS hsl() приводится к sRGB и не сохраняет цвет Display P3 за пределами sRGB.
  • Не храните значимый hue у серого с saturation 0%: угол не способен изменить отображаемый результат.
  • Не называйте изменение HSL lightness тинтом или затемнением, если проект не определил эти слова именно так.
  • Не подменяйте saturation прозрачностью: первая меняет непрозрачный цвет, вторая — композицию с фоном.
  • Не ждите равных визуальных расстояний от одинаковых шагов hue или lightness: HSL не перцептивно равномерен.
  • Не округляйте HSL многократно с перезаписью канонического RGB: дробные преобразования накапливают сдвиг каналов.

Есть и особенность сериализации. Браузер может вернуть разрешённый цвет через rgb(), хотя автор написал hsl(), потому что использованное значение находится в sRGB. В тестах сравнивайте итоговые каналы или заранее нормализованную форму, а не исходную строку. В дизайн-токенах явно укажите, являются ли координаты HSL каноническим редактируемым источником или лишь производным представлением.

07

Публикуйте HSL-токены вместе с проверяемым результатом

Практичная система токенов может использовать HSL, не выдавая каждый числовой шаг за безопасный. Храните семантические имена, координаты и разрешённые значения sRGB, а затем проверяйте каждую реальную пару foreground/background. В примере белый используется на action с lightness 40%, потому что коэффициент равен 5,57:1. Для полупрозрачного disabled-токена вывод о контрасте невозможен без известного фона.

css
:root {
  --action: hsl(210 100% 40%);       /* #0066CC */
  --action-hover: hsl(210 100% 32%);
  --action-disabled: hsl(210 20% 55% / 60%);
  --on-action: #FFFFFF;              /* 5,57:1 на --action */
}

.action {
  color: var(--on-action);
  background: var(--action);
}

.action:hover {
  background: var(--action-hover);
}
  • Храните одно каноническое значение и достаточную точность для воспроизведения разрешённых каналов.
  • Документируйте изменяемую координату и семантическую роль полученного токена.
  • Экспортируйте готовый HSL и разрешённый HEX или RGB для проверки между инструментами.
  • Отдельно проверяйте default, hover, active, focus, selected, disabled, светлую и тёмную темы.
  • Считайте контраст по итоговым скомпозированным цветам и сохраняйте неокруглённый коэффициент в аудите.
  • Используйте OKLCH или другое подходящее пространство, когда важнее перцептивно ровные шаги, а не знакомые регуляторы HSL.
  • Ссылайтесь на метод преобразования и спецификацию, чтобы будущие изменения генератора можно было объяснить.
S

Первоисточники

Фактические утверждения статьи проверены по этим опубликованным спецификациям.

  1. W3C CSS Color 4 — запись HSL
  2. W3C CSS Color 4 — преобразование HSL в sRGB
  3. W3C CSS Color 4 — не влияющие на результат компоненты цвета
  4. W3C CSS Color 4 — современная и устаревшая запись цвета
  5. W3C WCAG 2.2 — относительная яркость
  6. W3C WCAG 2.2 — контраст (минимальный)