Перевод первой части статьи A pragmatic guide to modern CSS colours - part one».

Автор: Kevin Powell, 07 октября 2025

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

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

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


Современный способ записи цветов

То, как мы записываем цвета в CSS, эволюционировало далеко за пределы привычных шестнадцатеричных кодов (hex) и функций hsl(). Даже функции rgb() и hsl(), которые вы уже знаете, теперь выглядят чуть иначе.

Не нужно добавлять «a»

Раньше для прозрачности приходилось использовать rgba(), добавляя альфа-канал:

/* чистый красный */
.red {
  color: rgb(255, 0, 0);
}

/* красный с 50 % прозрачности */
.red-50 {
  color: rgba(255, 0, 0, 0.5);
}

Теперь же можно просто:

/* чистый красный */
.red {
  color: rgb(255, 0, 0);
}

/* красный с 50 % прозрачности */
.red-50 {
  color: rgb(255, 0, 0, 0.5);
}

Нет необходимости использовать отдельную функцию rgba() — всё указывается через rgb() с четырьмя аргументами (красный, зелёный, синий, альфа). Поддержка браузеров, за исключением Internet Explorer, уже есть. (IE теперь почти не актуален.)

При этом старая форма с запятыми считается «устаревшей» и работает только для старых функций. Новые функции требуют нового синтаксиса.

Синтаксис через пробелы

Доступен синтаксис, в котором значения разделяются пробелами:

.red {
  color: rgb(255 0 0);
}

.blue {
  color: hsl(226 100% 50%);
}

Но есть нюанс: вы не можете просто добавить четвёртое число, как это было бы с запятыми. Вместо этого используется косая черта (/):

.red-50 {
  color: rgb(255 0 0 / 0.5);
}

.hsl-red-50 {
  color: hsl(0 100% 50% / 0.5);
}

Именно такой формат (с разделением пробелами и /) выбран в спецификации, поскольку он масштабируется — в новых функциях (например, color(), oklch(), hwb()) может быть больше значений перед альфа-значением, и / помогает чётко отделить значение прозрачности.

Изменения в hsl()

Как и в rgb(), теперь нет нужды писать hsla() для альфа:

.red {
  color: hsl(0deg 100% 50%);
}

.red-50 {
  color: hsl(0deg 100% 50% / .5);
}

Ещё одно изменение: когда используется синтаксис через пробел, единицы измерения становятся необязательными:

.red {
  color: hsl(0deg 100% 50%);
}

.also-red {
  color: hsl(0deg 100 50);
}

.another-red {
  color: hsl(0 100% 50%);
}

.this-is-red-too {
  color: hsl(0 100 50);
}

(Я лично предпочитаю сохранять %, потому что редакторы, как VS Code, показывают превью цвета лишь тогда, когда они есть.)

При использовании rgb() в безединичном варианте значения лежат в диапазоне от 0 до 255, но можно также использовать процентную форму:

.green {
  color: rgb(0 255 0);
}

.also-green {
  color: rgb(0 100% 0);
}

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


Относительные цвета (Relative Colours)

Самое интересное начинается, когда можно вычислять цвета относительно других.

Идея относительных цветов: берётся существующий цвет, и на его основе создаётся новый.

Пример:

.rgb-red {
  color: rgb(from #ff0000 r g b);
}

Здесь #ff0000 — базовый цвет (красный). r g b — переменные, которые содержат компоненты красного, зелёного и синего из этого цвета. То есть это просто rgb(255 0 0).

И можно добавить прозрачность:

.rgb-red-50 {
  color: rgb(from #ff0000 r g b / 0.5);
}

И самое удобное — относительные цвета можно применять к любым цветам:

.hsl-red {
  color: hsl(from #ff0000 h s l);
}

.hsl-red-50 {
  color: hsl(from #ff0000 h s l / 0.5);
}

Здесь h s l — компоненты оттенка, насыщенности и освещённости из базового цвета.

Это особенно удобно, когда у вас есть CSS-переменные (custom properties), и вы хотите получать версии цвета с меньшей непрозрачностью:

:root {
  --color-primary: #2563eb;
}

.semi-transparent-primary-background {
  background-color: hsl(from var(--color-primary) h s l / 0.75);
}

Быстрые трюки с относительными цветами

Вы можете динамически менять компоненты цвета — например, создавать более светлую или более тёмную версии:

:root {
  --base: hsl(217 73% 50%);
  --base-light: hsl(from var(--base) h s 75%);
  --base-dark: hsl(from var(--base) h s 25%);
}

Представьте уведомление (toast): основа цвета, тёмный текст, светлый фон, тень с прозрачностью — всё можно брать от одной переменной:

.toast {
  --toast-color: #222;
  color: hsl(from var(--toast-color) h s 15%);
  border: 2px solid var(--toast-color);
  background: hsl(from var(--toast-color) h s 90%);
  box-shadow: 0 12px 12px -8px hsl(from var(--toast-color) h s l / 0.325);
}
[data-toast="info"] {
  --toast-color: #0362fc;
}
[data-toast="error"] {
  --toast-color: hsl(0 100% 50%);
}

Отлично подходит для создания оттенков, теней и фоновых цветов — всё в чистом CSS, без внешних скриптов.


Тёмная и светлая темы — стало легче

Работа с темами (светлая / тёмная) всегда давала сложности. Часто используют CSS-переменные, и меняют их значения в зависимости от темы:

:root {
  --text-heading: #000;
  --text-body: #212121;
  --surface: #efefef;
}
.dark-theme {
  --text-heading: #fff;
  --text-body: #efefef;
  --surface: #212121;
}

Но часто есть как медиа-запрос prefers-color-scheme, так и переключатель вручную — и тогда цвета дублируются в двух местах. Это неудобно.

Теперь есть функция light-dark(), которая позволяет задать цвет для светлой и тёмной темы сразу:

:root {
  --text-heading: light-dark(#000, #fff);
  --text-body: light-dark(#212121, #efefef);
  --surface: light-dark(#efefef, #212121);
}

Для корректной работы нужно указать color-scheme: light dark, чтобы браузер применял обе темы:

:root {
  color-scheme: light dark;
  --text-heading: light-dark(#000, #fff);
  --text-body: light-dark(#212121, #efefef);
  --surface: light-dark(#efefef, #212121);
}

html[data-theme="light"] {
  color-scheme: light;
}

html[data-theme="dark"] {
  color-scheme: dark;
}

Ещё удобно: можно «зафиксировать» тему для конкретных компонентов. Например, у секции .hero, где фон — это изображение, и текст всегда должен быть белым:

.hero {
  color-scheme: light;
  background: url('a-light-image-that-needs-dark-text-on-it.webp');
}

Таким образом эта секция не будет подчиняться глобальному переключению темы.


Цветовые пространства (Color Spaces)

Цветовое пространство — это математическая модель, по которой определяется, как выглядят цвета. Существует множество таких моделей, и каждая немного по-своему вычисляет цвета.

Когда мы используем градиенты, важна интерполяция — как браузер «заполняет» промежуточные цвета между двумя точками. Раньше браузеры использовали только sRGB, что не всегда давало лучший результат визуально.

Теперь можно явно указать, в каком пространстве рассчитывать переход:

linear-gradient(in oklch 90deg, <color1>, <color2>)

Например, у автора существует плавный градиент от синего к красному, который в sRGB даёт «размытую» середину. Используя пространство oklch, он добился более насыщенного перехода без лишней «промежуточной» точки.

Также можно использовать longer hue, чтобы заставить градиент «обходить» весь спектр цвета, если нужен эффект радуги:

linear-gradient(in hsl longer hue 90deg, <color1>, <color2>)

Если не указывать пространство, берётся srgb по умолчанию.

Автор предпочитает oklch как рабочее пространство, но экспериментирует и с lch, oklab, lab, hwb, xyz. Всё зависит от конкретной задачи.


Когда нужен точный цвет

Иногда клиент даёт точную спецификацию, например, ярко-зеленый Pantone, который сложно воспроизвести обычным hex, rgb() или hsl(), потому что они работают в пространстве sRGB — с ограниченным охватом цветов (gamut). Современные устройства (особенно мобильные) поддерживают более широкие цветовые пространства, чем sRGB.

Для таких случаев существует функция color(), которая позволяет работать с расширенными цветовыми пространствами, например display-p3. Если браузер не поддерживает пространство, он отобразит наиболее близкий цвет из своих возможностей.

В инструментах разработчика (например, Chrome DevTools) вы можете увидеть, какой цвет браузер «срезал», если он попадает за рамки sRGB. В обычной практике display-p3 используется не часто, но его наличие — ценный инструмент для определённых задач.


Что дальше

В CSS сегодня есть ещё много интересного: функции color-mix(), более сложные приёмы с относительными цветами, новые функции как oklch() и многое другое. Но в этой части статьи мы разобрали те фичи, которые уже приносят реальную пользу разработчикам, не углубляющимся в цветовую теорию.

Во второй части Kevin Powell хочет рассказать о более сложных возможностях и применениях этих техник на практике.

Комментарии (4)


  1. Manshoo
    18.10.2025 17:08

    Очень жду второй части!!!


  1. jooher
    18.10.2025 17:08

    Прозрачность без rgba() — теперь можно писать rgb(255 0 0 / 0.5)

    Ура, наконец-то! Теперь можно сэкономить аж целую букву a.

    Всё вместе — CSS наконец стал удобным для цвета

    Да не стал он удобным. Ну добавились свистоперделки для цветодрочеров, которым sRGB не хватает, ну ок.

    А чего реально не хватает в CSS - это например конкатенации стилей, чтобы можно было не в хтмле писать богомерзкое style="alert color-red font-big", а прямо в цсс ".alert{...} & .color-red & .font-big"


    1. dom1n1k
      18.10.2025 17:08

      Да не в экономии одной буквы дело, а в единообразии.
      В процессе экспериментов бывает прозрачность убираешь и возвращаешь - и нужно то же самое с буквой 'a' делать.


      1. jooher
        18.10.2025 17:08

        Ну если в ходе экспериментов так тяжко букву а убрать, то можно просто 1 ставить для opacity.

        Да я и не против этих микроулучшений. Просто это все фигня на фоне общего уродства css.