Синтаксис относительного цвета CSS

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

Адам Аргайл
Adam Argyle

В Chrome 119 есть очень мощная функция цвета из CSS Color Level 5 . Синтаксис относительного цвета упрощает манипулирование цветом в CSS, предлагая авторам и дизайнерам следующие возможности:

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

:root {
  --brand-hue: 300deg;
  --brand-saturation: 75%;
  --brand-lightness: 50%;

  --brand-hsl:
    var(--brand-hue)
    var(--brand-saturation)
    var(--brand-lightness);

  --brand-color: hsl(var(--brand-hsl));

  /* all this work just so I can set the opacity to 50% in a variant */
  --brand-color-variant: hsl(var(--brand-hsl) / 50%);
}

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

:root {
  --brand-color: hsl(300deg 75% 50%);
  --brand-color-variant: hsl(from var(--brand-color) h s l / 50%);
}

Этот пост поможет вам изучить синтаксис и продемонстрировать общие манипуляции с цветом .

Если вы предпочитаете видео, почти вся следующая статья рассматривается в этом вызове GUI.

Обзор синтаксиса

Цель синтаксиса относительного цвета — позволить получить цвет из другого цвета. Базовый цвет называется исходным цветом, это цвет, который идет после ключевого слова new from . Браузер преобразует и разбивает исходный цвет на части и предлагает части в качестве переменных для использования в новом определении цвета.

Показана диаграмма синтаксиса rgb (от зеленого r g b / альфа): стрелка выходит из верхней части зеленого цвета и переходит в начало функции rgb. Эта стрелка разделяется на 4 стрелки, которые затем указывают на соответствующую переменную. 4 стрелки: красная, зеленая, синяя и альфа. Красный и синий имеют значение 0, зеленый — 128, а альфа — 100%.

На предыдущей диаграмме показано, как исходный green цвет преобразуется в цветовое пространство нового цвета, преобразуется в отдельные числа, представленные как переменные r , g , b и alpha , которые затем напрямую используются в качестве значений нового цвета rgb() .

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

Ключевое слово from

Первая часть синтаксиса, которую нужно изучить, — это часть from <color> до указания цвета. Это происходит прямо перед тем, как вы укажете значения. Вот пример кода, где все, что было добавлено, взято from green прямо перед указанием значений для rgb() .

.syntax-introduction_same-colors {
  color: green;
  color: rgb(0 128 0);
  color: rgb(from green r g b);    /* result = rgb(0 128 0) */
}

Ключевое слово from , если оно рассматривается как первый параметр в функциональной записи, превращает определение цвета в относительный цвет! После ключевого слова from CSS ожидает цвет, цвет, который вдохновит следующий цвет .

Преобразование цвета

Проще говоря, он преобразует зеленый цвет в каналы rg и b для использования в новом цвете.

rgb(from green r g b)           /* r=0 g=128 b=0 */
rgb(from rgb(0 128 0) r g b);   /* r=0 g=128 b=0 */

Цвета из пользовательских свойств

Чтение rgb from green очень четкое и легкое для понимания. Вот почему пользовательские свойства и синтаксис относительного цвета так хорошо сочетаются: вы можете раскрыть тайну цвета from . Вам также обычно не нужно знать цветовой формат пользовательского свойства color, поскольку вы создаете новый цвет в формате по вашему выбору.

rgb(from rgb(255 105 180) r g b) /* ????? */
rgb(from var(--hotpink) r g b)   /* clear */

Работайте в предпочитаемом вами цветовом пространстве

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

rgb(from hsl(120 100% 25%) r g b)     /*  r=0   g=128  b=0    */
hsl(from hsl(120 100% 25%) h s l)     /*  h=120 s=100% l=25%  */
hwb(from hsl(120 100% 25%) h w b)     /*  h=120 w=0%   b=50%  */
lch(from hsl(120 100% 25%) l c h)     /*  l=46  c=68   h=134  */

Синтаксис относительного цвета имеет этот этап преобразования; цвет после from преобразуется в цветовое пространство, указанное в начале относительного цвета. Входные и выходные данные не обязательно должны совпадать, что очень освобождает.

Возможность выбирать цветовое пространство также расширяет возможности, поскольку выбор цветового пространства, как правило, больше ориентирован на тип чередования цветов, чем на предпочтения. Предпочтение отдается результатам, а не цветовому формату или типам каналов. Это станет намного яснее в разделах, демонстрирующих варианты использования, поскольку разные цветовые пространства отлично подходят для разных задач.

Смешивайте, сопоставляйте, опускайте и повторяйте переменные

Что-то странное, но интересное в этом синтаксисе: переменные не нужно возвращать в порядок, и их можно повторять.

rgb(from green g g g)    /* rgb(128 128 128) */
rgb(from green b r g)    /* rgb(0 0 128) */
rgb(from green 0 0 g)    /* rgb(0 0 128) */

Непрозрачность как переменная

Синтаксис также обеспечивает непрозрачность в виде переменной с именем alpha . Это необязательно и идет после / в функциональном обозначении цвета.

rgb(from #00800080 r g b / alpha)             /* alpha=50% */
rgb(from rgba(0,128,0,.5) r g b / alpha)      /* alpha=50% */
rgb(from rgb(0 128 0 / 50%) r g b / alpha)    /* alpha=50% */

Используйте Calc() или другие функции CSS для переменных.

До сих пор мы создавали зеленый цвет снова и снова. Изучение синтаксиса, ознакомление с этапами преобразования и деструктуризации. Теперь пришло время изменить переменные, изменить выходные данные, чтобы они не совпадали с входными.

green                              /*  h=120 s=100% l=25%  */
hsl(from green calc(h * 2) s l)    /*  h=240 s=100% l=25%  */

Теперь это флот! Оттенок был удвоен, взяв оттенок 120 и превратив его в 240 , полностью изменив цвет. Это вращало оттенок вдоль цветового круга — изящный трюк, который можно сделать очень простым с помощью цилиндрических цветовых пространств, таких как HSL , HWB , LCH и OKLCH .

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

Проверьте поддержку браузера

@supports (color: rgb(from white r g b)) {
  /* safe to use relative color syntax */
}

Варианты использования и демонстрации

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

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

Все демо-версии можно найти в этой коллекции Codepen .

Осветлить цвет

Цветовые пространства OKLCH, OKLAB , XYZ или sRGB обеспечивают наиболее предсказуемые результаты при осветлении цветов.

Облегчить на величину

В следующем примере .lighten-by-25 берет blue цвет и преобразует его в OKLCH, затем осветляет синий, увеличивая канал l (яркость) путем умножения текущего значения на 1.25 . Это смещает яркость синего цвета к белому на 25%.

.lighten-by-25 {
  background: oklch(from blue calc(l * 1.25) c h);
}

Осветлить до определенного значения

В следующем примере .lighten-to-75 не используется канал l для осветления blue , вместо этого он полностью заменяет значение на 75% .

.lighten-to-75 {
  background: oklch(from blue 75% c h);
}

Затемнение цвета

Те же цветовые пространства, которые эффективны для осветления цвета, также отлично подходят для его затемнения.

Затемнить на величину

В следующем примере .darken-by-25 берет синий цвет и преобразует его в OKLCH, затем затемняет синий, уменьшая канал l (яркость) на 25 %, умножая значение на .75 . Это смещает синий цвет к черному на 25%.

.darken-by-25 {
  background: oklch(from blue calc(l * .75) c h);
}

Затемнение до указанного значения

В следующем примере .darken-to-25 не используется канал l для затемнения blue , вместо этого он полностью заменяет значение на 25% .

.darken-to-25 {
  background: oklch(from blue 25% c h);
}

Насыщенность цвета

Насытить на сумму

В следующем примере .saturate-by-50 используется s из hsl() для увеличения яркости orchid на относительные 50% .

.saturate-by-50 {
  background: hsl(from orchid h calc(s * 1.5) l);
}

Насытить до определенной суммы

В следующем примере .saturate-to-100 не используется канал s из hsl() , вместо этого он указывает желаемое значение насыщенности. В этом примере насыщенность увеличена до 100% .

.saturate-to-100 {
  background: hsl(from orchid h 100% l);
}

Обесцветить цвет

Обесцветить на величину

В следующем примере .desaturate-by-half использует s из hsl() для уменьшения насыщенности indigo вдвое.

.desaturate-by-half {
  background: hsl(from indigo h calc(s / 2) l);
}

Обесцветить до определенного значения

Вместо того, чтобы обесцвечивать на определенную величину, вы можете обесцветить до определенного желаемого значения. Следующий пример .desaturate-to-25 создает новый цвет на основе indigo , но устанавливает насыщенность на 25%.

.desaturate-to-25 {
  background: hsl(from indigo h 25% l);
}

Chroma усиливает цвет

Этот эффект похож на насыщение цвета, но имеет несколько отличий. Во-первых, это изменение chroma , а не изменение saturation , и это связано с тем, что цветовые пространства, которые могут расширяться до более высокого динамического диапазона, не используют насыщенность. Цветовые пространства, в которых присутствует chroma обладают широким динамическим диапазоном, что позволяет авторам повышать яркость цвета даже больше, чем это возможно при насыщении.

.increase-chroma {
  background: oklch(from orange l calc(c + .1) h);
}

Настройка непрозрачности цвета

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

Отрегулируйте непрозрачность на величину

.decrease-opacity-by-25 {
  background: rgb(from lime r g b / calc(alpha / 2));
}

Отрегулируйте непрозрачность до определенного значения

.decrease-opacity-to-25 {
  background: rgb(from lime r g b / 25%);
}

Инвертировать цвет

Инверсия цвета — это распространенная функция настройки цвета, которую можно найти в библиотеках цветов. Один из способов добиться этого — преобразовать цвет в RGB, а затем вычесть значение каждого канала из 1.

.invert-each-rgb-channel {
  background: rgb(from yellow calc(255 - r) calc(255 - g) calc(255 - b));
}

Дополните цвет

Если вашей целью было не инвертировать цвет, а дополнить его, то, скорее всего, вам нужно вращение оттенка. Выберите цветовое пространство, в котором оттенок представлен в виде угла, затем используйте calc() чтобы повернуть оттенок на нужную величину. Поиск дополнения цвета осуществляется вращением на пол-оборота, в этом случае для достижения результата можно добавить или вычесть из канала h на 180 .

.complementary-color {
  background: hsl(from blue calc(h + 180) s l);
}

Контрастный цвет

В качестве метода достижения доступного коэффициента цветового контраста рассмотрите L* (Lstar). При этом используется (приблизительно) перцептивно равномерный канал яркости (L) из LCH и OKLCH в calc() . В зависимости от того, нацелены ли вы на низкий, средний или высокий контраст, дельта L* составляет около ~40, ~50 или ~60.

Этот метод хорошо работает с любым оттенком в LCH или OKLCH.

Контраст более темного цвета

Класс .well-contrasting-darker-color демонстрирует L* с дельтой 60. Поскольку исходный цвет является темным (низкое значение яркости), к каналу яркости добавляется 60% (0,6). Этот метод используется для поиска контрастного темного цвета текста одинакового оттенка на светлом фоне.

.well-contrasting-darker-color {
  background: darkred;
  color: oklch(from darkred calc(l + .60) c h);
}

Контраст более светлого цвета

Класс .well-contrasting-lighter-color также демонстрирует L* с дельтой 60%. Поскольку исходный цвет является светлым (высокое значение яркости), из канала яркости вычитается 0,60.

.well-contrasting-lighter-color {
  background: lightpink;
  color: oklch(from lightpink calc(l - .60) c h);
}

Цветовые палитры

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

Откройте пример исходного кода и попробуйте изменить --base-color , чтобы увидеть, насколько динамичны эти палитры. Это весело!

Если вам нравится видео, я даю подробную информацию о построении цветовых палитр в CSS с помощью OKLCH на YouTube .

Монохроматические палитры

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

:root {
  --base-color: deeppink;

  --color-0: oklch(from var(--base-color) calc(l + .20) c h); /* lightest */
  --color-1: oklch(from var(--base-color) calc(l + .10) c h);
  --color-2: var(--base-color);
  --color-3: oklch(from var(--base-color) calc(l - .10) c h);
  --color-4: oklch(from var(--base-color) calc(l - .20) c h); /* darkest */
}
Попробуйте несколько палитр, созданных с использованием относительного цветового синтаксиса и OKLCH.

Open Props , библиотека бесплатных переменных CSS, предлагает цветовые палитры, созданные с помощью этой стратегии, и упрощает их использование при импорте. Кроме того, все они созданы на основе цвета, который вы можете настроить: вы просто задаете ему цвет, и он выдает палитру!

Аналоговые палитры

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

:root {
  --base-color: blue;

  --primary:   var(--base-color);
  --secondary: oklch(from var(--base-color) l c calc(h - 45));
  --tertiary:  oklch(from var(--base-color) l c calc(h + 45));
}

Триадные палитры

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

Это небольшое упрощение теории цвета, но этого достаточно, чтобы начать изучать более сложные триадные палитры, если вам интересно.

:root {
  --base-color: yellow;
  --triad-1: oklch(from var(--base-color) l c calc(h - 120));
  --triad-2: oklch(from var(--base-color) l c calc(h + 120));
}

Тетрадные палитры

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

Это небольшое упрощение теории цвета, но этого достаточно, чтобы начать изучать более сложные тетрадные палитры, если вам интересно.

:root {
  --base-color: lime;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) l c calc(h + 90));
  --color-3: oklch(from var(--base-color) l c calc(h + 180));
  --color-4: oklch(from var(--base-color) l c calc(h + 270));
}

Монохроматический с небольшим вращением оттенков

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

В следующем примере яркость уменьшается на 10% для каждого образца, а также поворачивается оттенок на 10 градусов. В результате получилась палитра от ярко-розового до индиго, которая, кажется, плавно сочетается, как градиент.

:root {
  --base-color: deeppink;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) calc(l - .10) c calc(h - 10));
  --color-3: oklch(from var(--base-color) calc(l - .20) c calc(h - 20));
  --color-4: oklch(from var(--base-color) calc(l - .30) c calc(h - 30));
  --color-5: oklch(from var(--base-color) calc(l - .40) c calc(h - 40));
}
Попробуйте эту таблицу лидеров, созданную с помощью OKLCH и ротации оттенков.

Следующий интерфейс таблицы лидеров использует эту стратегию ротации оттенков. Каждый элемент списка отслеживает свой индекс в документе как переменную с именем --i . Этот индекс затем используется для регулировки цветности, яркости и оттенка. Регулировка составляет всего 5% или 5 градусов, что намного тоньше, чем в приведенном выше примере с темно-розовым, поэтому требуется острый глаз, чтобы заметить, почему эта таблица лидеров может быть любого оттенка с такой элегантностью.

Обязательно измените оттенок ползунка под таблицей лидеров, и вы увидите, как относительный синтаксис цветов создает красивые цветовые моменты.

li {
  --_bg: oklch(
    /* decrease lightness as list grows */
    calc(75% - (var(--i) * 5%))

    /* decrease chroma as list grows */
    calc(.2 - (var(--i) * .01))

    /* lightly rotate the hue as the list grows */
    calc(var(--hue) - (var(--i) + 5))
  );
}