CSS 相对颜色语法

根据另一种颜色的通道和值创建新颜色。

Adam Argyle
Adam Argyle

Chrome 119 中新增了 CSS 颜色级别 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 挑战视频,其中涵盖了本文中的几乎所有内容。

语法概览

相对颜色语法的目标是允许从另一种颜色派生颜色。基础颜色称为来源颜色,这是新 from 关键字后面的颜色。浏览器会转换并拆解此原始颜色,并将各个部分作为变量提供,以便在新的颜色定义中使用。

该图显示了语法 rgb(来自绿色 r g b / alpha)的示意图,其中有一个箭头从绿色顶部伸出并进入函数 RGB 的开头,该箭头拆分为 4 个箭头,然后指向其相关变量。4 个箭头分别是红色、绿色、蓝色和 alpha 值。红色和蓝色的值为 0,绿色为 128,Alpha 为 100%。

上图显示了将源颜色 green 转换为新颜色的颜色空间,并将其转换为以 rgbalpha 变量表示的各个数字,然后这些数字会直接用作新 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 预期会看到颜色,即为下一个颜色提供灵感的颜色

颜色转换

简单来说,它会将绿色转换为 r g 和 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 颜色的神秘面纱。此外,您通常无需知道自定义属性颜色的颜色格式,因为您将以您选择的格式创建新颜色。

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,从而完全改变了颜色。这会沿着颜色轮旋转色相,这是一个非常巧妙的技巧,借助 HSLHWBLCHOKLCH圆柱形颜色空间,可以轻松实现。

如需直观地查看各个通道的值,以便您不必猜测或记住规范,就能准确计算出结果,请试用此相对颜色语法通道值工具。它会根据您指定的语法显示每个渠道的值,以便您准确了解可以使用哪些值。

检查浏览器支持情况

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

用例和演示

以下示例和用例具有许多替代语法,可用于实现类似或相同的结果。这些差异源自色彩空间及其提供的频道。

此外,许多示例都将使用 byto 的字词来显示色彩调整。颜色更改 by 是相对颜色更改;这种更改会使用变量的值,并根据其当前值进行调整。颜色更改 to 是绝对颜色更改;这种更改不会使用变量的值,而是指定完全新的值。

所有演示都可以在此 Codepen 集合中找到。

使颜色变浅

在亮化颜色时,OKLCH、OKLABXYZsRGB 颜色空间可提供最可预测的结果。

按百分比调亮

以下示例 .lighten-by-25 会获取颜色 blue 并将其转换为 OKLCH,然后通过将当前值乘以 1.25 来增加 l(亮度)通道,从而使蓝色变浅。这会将蓝色的亮度向白色推移 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 使用 hsl() 中的 sorchid 的饱和度提高了相对 50%

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

饱和到特定数量

以下示例中的 .saturate-to-100 不会使用 hsl() 中的 s 通道,而是指定所需的饱和度值。在本例中,饱和度提高至 100%

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

降低颜色饱和度

按百分比去饱和

以下示例 .desaturate-by-half 使用 hsl() 中的 sindigo 的饱和度减半。

.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 更改,而不是 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() 按所需量旋转色相。查找颜色的补色是通过旋转半圈来完成的,在本例中,您可以通过 180h 通道进行加法或减法运算来实现该结果。

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

颜色对比

如需实现方便轻松查看的颜色对比度,不妨考虑使用 L&middot; (Lstar)。 这会在 calc() 中使用来自 LCH 和 OKLCH 的(近似)感知均匀的亮度 (L) 通道。根据您定位的对比度(低、中或高),L&middot; 差值约为 40、50 或 60。

该技术适用于 LCH 或 OKLCH 的任何色调。

与较深的颜色形成对比

.well-contrasting-darker-color 类演示了增量为 60 的 L*。由于源颜色是深色(低亮度值),因此系统会将 60% (0.6) 添加到亮度通道。此方法用于在浅色背景上查找对比明显、色调相同、色调较暗的文本颜色。

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

对比浅色

.well-contrasting-lighter-color 类还演示了 Delta 为 60% 的 L*。由于源颜色是浅色(高价值亮度),因此会从亮度通道中减去 0.60。

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

调色板

相对颜色语法非常适合创建调色板。由于可用的颜色空间众多,它尤其有用且强大。以下示例均使用 OKLCH,因为亮度通道可靠,并且可以旋转色相通道而不会产生副作用。最后一个示例演示了如何结合使用亮度和色相旋转调整,以获得更有趣的结果!

打开这些示例的源代码,然后尝试更改 --base-color,看看这些调色板的动态效果。它很有趣!

如果您喜欢视频,可以观看我在 YouTube 上介绍如何使用 OKLCH 在 CSS 中构建调色板的视频,了解详细信息。

单色调色板

创建单色调色板是指使用相同色调创建调色板,但亮度和暗度有所不同。中间颜色是调色板的源色,两侧各有两个更浅和更深的变体。

: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));
}

三元色调色板

与互补色类似,三等分色调色板是相对的,但适用于给定基本颜色的和谐色调旋转。互补色位于某种颜色的对面,就像通过色轮中心绘制的一条直线一样;三元色调色板就像由三条线组成的三角形,通过从基色等距旋转找到 2 种颜色。通过旋转色相 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))
  );
}