现代 CSS 颜色指南:RGB、HSL、HWB、LAB 和 LCH


网页上的颜色远不止表面看到的那么简单,而且它即将变得更加有趣!今天,我们将看看在设计系统中使用颜色的最佳方法,以及未来不久我们可以期待的颜色发展。

熟悉的颜色值

在 CSS 中定义颜色有许多不同的方法。CSS 命名颜色是为元素上色最简单的方式之一:

1
2
3
.my-element {
background-color: red;
}

这些颜色非常有限,且很少能适应我们正在构建的设计!我们还可以使用颜色的十六进制值。这段代码为我们的元素赋予了红色背景:

1
2
3
.my-element {
background-color: #ff0000;
}

除非你是颜色专家,否则十六进制值非常难以读取。通过查看十六进制值,你很难猜出一个元素的颜色。构建网站时,设计师可能会给我们提供一个十六进制颜色值,但如果他们要求我们将其颜色变深 20%,没有视觉引导或颜色选择器的情况下,我们将很难通过调整十六进制值来做到这一点。

RGB

RGB(红、绿、蓝)表示法是定义颜色的另一种方式,它与十六进制值提供了相同范围的颜色,但更易读。在 CSS 中,我们可以使用 rgb() 函数来实现。网页上的颜色是加法混合的,意味着红、绿和蓝的比例越高,结果颜色就越亮。如果我们只使用红色通道,结果将是红色:

1
2
3
.my-element {
background-color: rgb(255, 0, 0);
}

将红色、绿色和蓝色通道设置为最高值将产生白色:

1
2
3
.my-element {
background-color: rgb(255, 255, 255);
}

我们还可以通过使用 rgba() 函数添加一个 alpha 通道(透明度):

1
2
3
4
5
6
7
.my-element {
background-color: rgba(255, 0, 0, 0.5); // 50% 透明
}

.my-element {
background-color: rgba(255, 0, 0, 1); // 完全不透明
}

rgb()rgba() 允许我们在代码中“混合”颜色,但结果有时可能难以预测。

HSL

最近,我们可以使用 HSL(色相、饱和度、亮度)值,通过 hsl()hsla() 函数来设置颜色。作为开发者,这些值在调整颜色时更加直观。例如,我们可以通过调整亮度参数,得到同一颜色的深色和浅色变体:

1
2
3
4
5
6
7
8
9
10
11
.my-element {
background-color: hsl(0deg, 100%, 20%); // 深红色
}

.my-element {
background-color: hsl(0deg, 100%, 50%); // 中红色
}

.my-element {
background-color: hsl(0deg, 100%, 80%); // 浅红色
}

色相参数表示颜色轮上的位置,取值范围可以在 0 到 360 度之间。该函数还接受 turn 单位(例如 0.5turn)以及无单位的值。

以下都是有效的代码:

1
2
3
4
5
6
7
8
9
10
11
.my-element {
background-color: hsl(180deg, 50%, 50%);
}

.my-element {
background-color: hsl(0.5turn, 50%, 50%);
}

.my-element {
background-color: hsl(180, 50%, 50%);
}

提示:在 Chrome 和 Firefox 开发工具的检查器中,按住 SHIFT 并点击颜色样本,可以在十六进制、RGB 和 HSL 之间切换颜色值!

hsl()hsla() 非常适合与自定义属性一起进行操作,我们稍后将看到这一点。

currentColor 关键词值得一提,它是另一种为元素设置颜色的方式,已经存在了一段时间。它允许我们有效地将元素当前的文本颜色用作一个变量。与自定义属性相比,它的功能相对有限,但常用于设置 SVG 图标的填充颜色,以确保它们与父元素的文本颜色匹配。

现代颜色语法

CSS 色彩模块 Level 4 为我们的颜色函数提供了更方便的语法,已在各大浏览器中广泛支持。我们不再需要用逗号分隔各个值,并且 rgb()hsl() 函数可以接受一个可选的 alpha 参数,用斜杠来分隔:

1
2
3
4
5
6
7
8
9
.my-element {
/* 可选的 alpha 值使得透明度为 50% */
background-color: hsl(0 100% 50% / 0.5);
}

.my-element {
/* 没有 alpha 值时,背景是完全不透明的 */
background-color: hsl(0 100% 50%);
}

新的 CSS 颜色函数

HWB

HWB 代表色相、白度和黑度。与 HSL 类似,色相可以在 0 到 360 之间的任何范围内。其余两个参数控制在该色相中混入多少白色或黑色,最大可达 100%(这将导致颜色变为完全白色或完全黑色)。如果同时混入等量的白色和黑色,颜色会变得越来越灰。我们可以将其类比为调和油漆,这在创建单色调色盘时特别有用。

LAB

LAB 和 LCH 在规范中定义为设备无关的颜色空间。LAB 是一种颜色空间,可在 Photoshop 等软件中访问,如果你希望颜色在屏幕上和印在 T 恤上看起来相同,LAB 是推荐的选择。它使用三个轴:亮度、a 轴(从绿色到红色)和 b 轴(从蓝色到黄色)。

亮度以百分比表示,类似于 HSL,但使用 lab() 函数时亮度可以超过 100%。超亮的白色可以使用高达 400% 的百分比。a 轴和 b 轴的值可以为正或负。两个负值将导致颜色向光谱的绿色/蓝色端移动,而两个正值则会呈现更橙/红色的色调。

1
2
3
4
5
6
7
.my-element {
background-color: lab(80% 100 50); // 红粉色
}

.my-element {
background-color: lab(80% -80 -100); // 蓝绿松石色
}

LCH

LCH 代表亮度(lightness)、色度(chroma)和色相(hue)。与 LAB 类似,亮度可以超过 100%。色相范围在 0 到 360 之间,色度表示颜色的强度,类似于 HSL 中的饱和度,但色度可以超过 100——实际上它在理论上是无限的。示例:

1
2
3
4
5
6
7
.my-element {
background-color: lch(80% 100 50);
}

.my-element {
background-color: lch(80% 240 50); // 这种颜色超出了当今浏览器可显示的范围
}

然而,当前浏览器和显示器可以显示的颜色范围有限(稍后会详细说明),因此超过 230 的值可能不会产生任何差异——色度会被降低到可显示的范围内。

为什么需要 LAB 和 LCH 而不仅仅是 HSL?一个原因是使用 LAB 或 LCH 可以让我们访问更广泛的颜色范围。LCH 和 LAB 旨在覆盖人类视觉的整个光谱。而且,HSL 和 RGB 存在一些缺陷:它们不是感知上均匀的,在 HSL 中,增加或减少亮度对不同的色相产生的效果不同。

在这个示例中,我们可以通过灰度切换看到 LCH 和 HSL 的明显对比。对于 HSL 的色相和饱和度条,虽然 HSL 函数的“亮度”成分相同,但每个方块的感知亮度明显不同!而在 LCH 的色度和色相条上,感知亮度几乎是均匀的。

我们还可以看到在渐变中使用 LCH 颜色时的巨大差异。这两个渐变的起始颜色和结束颜色相同(使用此转换器将 LCH 值转换为 HSL 等效值)。但 LCH 渐变在中间呈现出充满活力的蓝色和紫色,而 HSL 渐变相比之下显得更加浑浊和失去鲜艳感。

  • LCH 和 HSL 的蓝到粉渐变条

虽然 LAB 和 LCH 在语法上可能不太直观,但它们的表现方式更符合人眼的感知。《CSS 中的 LCH 颜色:什么、为什么以及如何?》中,详细解释了 LCH 颜色的优势。 LCH 颜色选择器

与其他颜色函数类似,hwb()lab()lch() 也可以接受可选的 alpha 参数:

1
2
3
.my-element {
background-color: lch(80% 240 50 / 0.5); // 最终颜色的透明度为 50%
}

浏览器支持与色彩空间

目前,hwb()lab()lch() 只在 Safari 中得到支持。我们可以通过为不支持的浏览器提供回退方案来立即开始使用它们。不支持这些颜色函数的浏览器将简单地忽略第二条规则:

1
2
3
4
5
6
.my-element {
background-color: lch(55% 102 360);

/* 使用 Lea Verou 的工具将 LCH 颜色转换为 RGB:https://css.land/lch/ */
background-color: rgb(98.38% 0% 53.33%);
}

如果其他样式依赖于支持较新的颜色函数,我们可以使用功能查询:

1
2
3
4
5
6
7
8
9
10
11
.my-element {
display: none;
}

/* 只有在浏览器支持 lch() 时才显示此元素 */
@supports (background-color: lch(55% 102 360)) {
.my-element {
display: block;
background-color: lch(55% 102 360);
}
}

值得注意的是,尽管现代显示器能够显示超越 RGB 的颜色,但大多数浏览器目前仅支持 sRGB 色彩空间内的颜色。在 LAB 颜色演示中,你可能会注意到即使在支持 lab()lch() 的 Safari 中,移动滑块超出某个点后,颜色实际上并不会改变。使用超出 sRGB 范围的值只有在硬件和浏览器足够先进时才会生效。

Safari 现在支持 color() 函数,使我们能够在 P3 色彩空间中显示颜色,但目前这些仅限于 RGB 颜色,尚未提供 LAB 和 LCH 的所有优势。

1
2
3
4
.my-element {
background: rgb(98.38% 0% 53.33%); // 鲜亮的粉色
background: color(display-p3 0.947 0 0.5295); // P3 色彩空间的等效颜色
}

推荐阅读:《CSS 中的宽色域颜色与 Display-P3》

无障碍性

一旦广泛支持,LAB 和 LCH 或许能够帮助我们选择更具可访问性的颜色组合。只要亮度值保持不变,前景文本与背景颜色的对比度在不同的色相或色度值下应该是相同的。当前的 HSL 颜色显然还未实现这一点。

颜色管理

更多的颜色函数意味着我们在应用程序中管理颜色时有了更多选择。通常,我们需要在设计系统中生成某个颜色的多个变体,从深色到浅色。

自定义属性

CSS 自定义属性允许我们在样式表中存储值以供重用。由于它们允许部分属性值,因此在管理和操作颜色值时非常有用。HSL 尤其适合自定义属性,因为其直观性。在之前的演示中,我使用它们根据元素的索引(定义在另一个自定义属性中)通过计算 --hue 值来调整色条每个部分的色调。

1
2
3
4
li {
--hue: calc(var(--i) * (360 / 10));
background: hsl(var(--hue, 0) 50% 45%);
}

我们还可以计算互补色(色轮上相对的颜色)。关于这点已经有很多文章讨论,因此我在这里不再赘述。如果你感兴趣,可以参考 Sara Soueidan 的文章《使用 HSL 进行颜色管理》,这是一个不错的入门点。

从 HEX/RGB 迁移到 HSL

RGB 颜色可能在某种程度上满足你的需求,但如果你需要根据基础色彩调色板灵活地派生新的色调,那么切换到 HSL(或一旦支持的 LCH)可能是更好的选择。我建议你结合自定义属性使用这一方法。

注意:有很多在线资源可以将 HEX 或 RGB 值转换为 HSL(例如这个转换器)。

假设你已经将颜色存储为 Sass 变量:

1
$primary: rgb(141 66 245);

在转换为 HSL 时,我们可以为色调、饱和度和亮度值分配自定义属性。这样,我们可以轻松创建原始颜色的深色或浅色、更饱和或不太饱和的变体。

1
2
3
4
5
6
7
8
9
:root {
--h: 265;
--s: 70%;
--l: 50%;

--primary: hsl(var(--h) var(--s) var(--l));
--primaryDark: hsl(var(--h) var(--s) 35%);
--primaryLight: hsl(var(--h) var(--s) 75%);
}

HSL 对于创建色彩方案非常有用,Adam Argyle 的文章《构建色彩方案》中详细介绍了这一点。文中他使用品牌色作为基础,创建了浅色、深色和暗色方案。我喜欢这种方法,因为它允许对颜色变体进行细致控制(例如,在“暗色”方案中减少颜色的饱和度),同时保留了自定义属性的主要优势:只需在一个地方更新品牌颜色,所有颜色方案都会随之更新,这可能会为我们节省大量的工作。

SASS 颜色函数

在混合和调整颜色方面,Sass 多年来提供了颜色函数,帮助我们实现这些功能。我们可以饱和或去饱和、变亮或变暗,甚至将两种颜色混合。这些方法在某些情况下效果很好,但也有一些局限性:首先,我们只能在编译时使用它们,不能在浏览器中实时操作颜色。其次,它们仅限于 RGB 和 HSL,因此会遇到与感知一致性相关的问题。正如我们在这个演示中所看到的,颜色逐渐去饱和后,转换为灰度时看起来却变得越来越亮。

1
2
3
4
li {
--hue: calc(var(--i) * (360 / 10));
background: lch(50% 45 var(--hue, 0));
}

颜色混合与操作

颜色混合

CSS 目前还不支持在浏览器中混合颜色,但这即将改变:CSS Color Level 5 规范(工作草案)中包含了颜色混合函数的提案,听起来很有希望。第一个是 color-mix() 函数,它类似于 Sass 的 mix() 函数,但 color-mix() 在 CSS 中允许我们指定一个颜色空间,并默认使用 LCH,从而实现更好的混合效果。

更新:color-mix()color-contrast() 现在在 Safari 15 中可以通过启用一个标志来使用!查看这篇文章了解如何在 Safari 中启用实验性功能。

颜色作为参数传递时不必是 LCH,但插值将使用指定的颜色空间。我们可以像设置渐变停靠点一样指定要混合的每种颜色的比例:

1
2
3
4
5
6
7
8
9
.my-element {
/* 红色和蓝色等量混合 */
background-color: color-mix(in lch, red, blue);
}

.my-element {
/* 30% 红色,70% 蓝色 */
background-color: color-mix(in lch, red 30%, blue);
}
颜色对比与可访问性

color-contrast() 是另一个提议的函数,它在选择可访问的颜色方面具有巨大影响。事实上,它的设计初衷就是为了可访问性。它允许浏览器通过比较另一种颜色,从列表中选择最合适的值。我们甚至可以指定所需的对比度,以确保我们的色彩方案符合 WCAG 准则。颜色从左到右进行评估,浏览器选择第一个符合所需对比度的颜色。如果没有颜色符合要求,则选择对比度最高的颜色。

1
2
3
4
.my-element {
color: wheat;
background-color: color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA);
}

由于目前还没有浏览器支持该功能,我从规范中直接借用了这个示例。当浏览器评估该表达式时,结果颜色将是 darkgreen,因为它是第一个在与 wheat 比较时达到 AA 对比度的颜色。

浏览器支持

CSS Color Level 5 规范目前处于工作草案阶段,意味着还没有浏览器支持 color-contrast()color-mix() 函数,其语法也可能会发生变化。但可以预见,Web 上的颜色未来非常光明!

颜色的环境影响

你知道你选择的颜色调色板会影响你的网站耗能吗?在 OLED 屏幕(包括大多数现代电视和笔记本电脑)上,深色比浅色消耗的能量要少得多——白色消耗最多的能量,黑色最少。蓝色也比红色和绿色光谱区域的颜色消耗更多能量。为了减少你的应用程序对环境的影响,考虑采用深色配色方案,减少使用蓝色,或者为用户提供暗模式选项。作为额外的好处,更环保的颜色选择还可以减少对移动设备电池寿命的影响。