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

在相对颜色语法之后,您可以使用所需的任何颜色空间或语法创建品牌颜色,然后用更少的代码创建半不透明的变体。此外,它也更容易解读样式和系统的 intent。

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

这篇博文将帮助您了解语法演示常见的颜色操作

如果您更喜欢视频,此 GUI 挑战中涵盖了以下几乎所有文章。

语法概览

相对颜色语法的目标是允许从另一种颜色派生颜色。基色称为原色,是新的 from 关键字后面的颜色。浏览器将进行转换并拆分这种原始颜色,并以变量的形式提供这些部分,以便在新的颜色定义中使用。

系统显示了语法 rgb(from green 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,然后通过将该值乘以 .75,将 l(亮度)通道调低 25%,从而调暗蓝色。这会使蓝色向黑色方向推动 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&midast; (Lstar)。 它在 calc() 中使用来自 LCH 和 OKLCH 的(近似)感知均匀亮度 (L) 通道。根据您定位的是低、中、高对比度,L 增量约为 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 类也演示了增量为 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))
  );
}