根据其他颜色的通道和值创建新颜色。
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
关键字后面的颜色。浏览器会转换并拆解此原始颜色,并将各个部分作为变量提供,以便在新的颜色定义中使用。
上图展示了原始颜色 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 会预期一个颜色,这个颜色将为下一个颜色提供灵感。
颜色转换
简单来说,它会将绿色转换为 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
,完全改变了颜色。这会沿着颜色轮旋转色相,这是一个非常巧妙的技巧,借助 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,然后通过将当前值乘以 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()
中的 s
将 orchid
的振动度提高了相对 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()
中的 s
将 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
更改,而不是 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()
按所需量旋转色相。查找颜色的补色是通过旋转半圈来完成的,在本例中,您可以通过 180
对 h
通道进行加法或减法运算来实现该结果。
.complementary-color {
background: hsl(from blue calc(h + 180) s l);
}
对比颜色
如需实现方便轻松查看的颜色对比度,不妨考虑使用 L· (Lstar)。
这使用 calc()
中 LCH 和 OKLCH 中的(近似)感知均匀的亮度 (L) 通道。根据您定位的对比度(低、中或高),L· 差值约为 40、50 或 60。
此方法适用于 LCH 或 OKLCH 中的任何色相。
与较深的颜色形成对比
.well-contrasting-darker-color
类演示了 delta 为 60 的 L*。由于原始颜色是深色(亮度值较低),因此系统会向亮度通道添加 60%(.6)。此方法用于在浅色背景上找到与浅色背景形成鲜明对比的深色文本颜色。
.well-contrasting-darker-color {
background: darkred;
color: oklch(from darkred calc(l + .60) c h);
}
与较浅的颜色形成对比
.well-contrasting-lighter-color
类还演示了 Delta 为 60% 的 L*。由于原始颜色是浅色(亮度值较高),因此从亮度通道中减去 .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))
);
}