CSS 颜色模块 第4级

W3C 候选推荐草案,

关于本文件的更多详情
此版本:
https://www.w3.org/TR/2025/CRD-css-color-4-20250424/
最新发布版本:
https://www.w3.org/TR/css-color-4/
编辑草案:
https://drafts.csswg.org/css-color-4/
先前版本:
历史:
https://www.w3.org/standards/history/css-color-4/
实现报告:
https://wpt.fyi/results/css/css-color
反馈:
CSSWG 问题库
编辑:
Tab Atkins Jr. (谷歌)
Chris Lilley (W3C)
Lea Verou (特邀专家)
前任编辑:
L. David Baron (Mozilla)
为此规范建议编辑:
GitHub 编辑器
测试套件:
https://wpt.fyi/results/css/css-color/

摘要

本规范描述了 CSS 中的 <color> 值,以及用于前景颜色和组不透明度的属性。

CSS 是一种用于描述结构化文档(例如 HTML 和 XML)在屏幕、纸张等上的呈现的语言。

本文档的状态

本节描述了本文档在发布时的状态。 W3C 当前出版物的列表以及本技术报告的最新修订版可以在 W3C 标准和草案索引 (https://www.w3.org/TR/) 中找到。

本文档由 CSS 工作组作为候选推荐标准草案发布,采用推荐标准流程。 作为候选推荐标准发布并不意味着 W3C 及其成员的认可。 候选推荐标准草案整合了先前候选推荐标准中的更改,工作组打算将其包含在后续的候选推荐标准快照中。

这是一份草案文件,可能随时被其他文件更新、替换或废弃。 将此文件引用为正在进行的工作以外的内容是不合适的。

请通过在 GitHub 中提交问题(首选)发送反馈, 并在标题中包含规范代码“css-color”,如下所示: “[css-color] …评论摘要…”。 所有问题和评论都已存档。 或者,可以将反馈发送到(已存档)公共邮件列表 www-style@w3.org

本文档受 2023 年 11 月 3 日 W3C 流程文档的约束。

本文档由一个在 W3C 专利政策下运作的小组制作。 W3C 维护一份与该小组的可交付成果相关的任何专利披露的公开列表; 该页面还包括披露专利的说明。 实际知晓某项专利的个人,如果认为该专利包含基本权利要求, 则必须根据 W3C 专利政策第 6 节披露该信息。

1. 引言

本节不是规范性的。

测试

本节不是规范性的,不需要测试。


本模块描述了 CSS 属性,这些属性允许作者指定元素文本内容的前景颜色和不透明度。 本模块还详细描述了 CSS <color> 值类型。

它不仅定义了已经存在于 CSS1CSS2CSS Color 3 中的与颜色相关的属性和值, 还定义了新的属性和值。

特别是,它允许在 sRGB 之外的其他 颜色空间 中指定颜色; 以前,即使显示设备支持,更饱和的颜色(超出 sRGB 色域的颜色)也不能在 CSS 中使用。

可用的 草稿实现报告 已发布。

1.1. 值的定义

本规范遵循 CSS 属性定义惯例,来自 [CSS2],使用 值定义语法,来自 [CSS-VALUES-3]。 本规范未定义的值类型在 CSS 值与单位 [CSS-VALUES-3] 中定义。 与其他 CSS 模块的组合可能会扩展这些值类型的定义。

除了在定义中列出的特定于属性的值外,本规范中定义的所有属性还接受 CSS-wide 关键字 作为其属性值。 为了可读性,它们没有被显式重复。

2. 颜色术语

测试

本节提供后续使用的定义,不需要测试。


颜色 是对人类视觉对光线或被光照亮的物体的视觉感知的定义(数值或文本)。 对人类颜色感知的客观研究称为 颜色测量学

物体的颜色取决于它在每个可见波长上反射的光的多少, 以及照亮它的光的实际颜色(同样是每个波长上的光量)。 它通过分光光度计进行测量。

发光物体的颜色(包括计算机屏幕上的颜色) 取决于它在每个可见波长上发射的光的多少。 它通过分光辐射计进行测量。

如果两个物体具有不同的光谱,但仍然产生相同的物理感觉, 我们说它们具有相同的颜色。 我们可以通过将光谱转换为 CIE XYZ(三个数字)来计算两个颜色是否相同。

例如,一个绿色的叶子,显示在计算机屏幕上的该叶子的照片, 以及该照片的打印版, 都通过不同的方式产生绿色的感觉。 如果屏幕和打印机是校准的, 则叶子、照片和打印的绿色看起来相同。

颜色空间 是对颜色相对于基础颜色测量学模型的组织, 使得在该颜色空间中任何颜色都有明确的、可客观测量的含义。 这也意味着相同的颜色可以在多个颜色空间中表示, 或从一个颜色空间转换为另一个颜色空间, 同时看起来保持相同。

使用分光光度计对叶子进行测量,发现颜色为 lch(51.2345% 21.2 130), 也就是 lab(51.2345% -13.6271 16.2401)。

同样的颜色可以在不同的颜色空间中表示:

 color(sRGB 0.41587 0.503670 0.36664);
 color(display-p3 0.43313 0.50108 0.37950);
 color(a98-rgb 0.44091 0.49971 0.37408);
 color(prophoto-rgb 0.36589 0.41717 0.31333);
 color(rec2020 0.42210 0.47580 0.35605);

加色色彩空间意味着坐标系在光强度上是线性的。 CIE XYZ 色彩空间是一种加色色彩空间。 XYZ 的 Y 分量是亮度,即单位面积的光强度, 或者说“它有多亮”。亮度以坎德拉每平方米 (cd/m²) 为单位进行测量, 也称为尼特

在加色色彩空间中,可以进行计算 以准确预测颜色混合。大多数 RGB 空间 不是加色的,因为其分量是伽马编码的。取消这种伽马编码 会产生线性光值。

例如,如果一个灯具包含两个相同的彩色灯光, 只有一个打开时,测得的颜色是 color(xyz 0.13 0.12 0.04), 那么当两个灯光都打开时,颜色将正好是前者的两倍,即 color(xyz 0.26 0.24 0.08)。

如果我们有两种不同颜色的聚光灯照射在舞台上, 一种测得的值是 color(xyz 0.15 0.24 0.17), 另一种是 color(xyz 0.11 0.06 0.06), 那么我们可以准确预测,如果两个有色光束重叠, 混合后的颜色将是 XYZ 分量值的总和,即 color(xyz 0.26 0.30 0.23)。

色度 是将亮度分量排除后的颜色测量值。 从上述相同的灯光示例中,一个灯光的u',v' 色度为 (0.2537, 0.5268), 而两个灯光的色度相同(它们的颜色相同,只是更亮)。

色度是加性的, 因此它可以准确预测混合物的色度(但不能预测其产生的亮度)。 色度是二维的,因此可以在色度图上轻松表示,以预测颜色混合的色度。 任何两个颜色都可以混合,结果颜色将位于图上连接它们的直线上。 三种颜色形成一个平面,结果颜色将位于它们在图上形成的三角形内。

显示-p3 颜色空间的 uv 色度图
一个色度图,显示了(实色)display-p3 颜色空间, 并用于对比显示(淡色)的 sRGB 颜色空间。 还显示了白点(D65)。

因此,一旦线性化,RGB 色彩空间就是加色的, 它们的色域由红、绿、蓝三原色的色度, 加上白点(所有三种原色都达到最大强度时形成的颜色)的色度定义。

大多数色彩空间使用少数几种模拟日光的白点之一, 这些白点以相应黑体辐射器的相关色温 (CCT) [Understanding_CCT] 命名。 例如,D65 是一个日光白点, 对应于 6500 开尔文的相关色温 (实际上是 6504, 因为自从最初定义该颜色以来,普朗克常数的值已发生变化)。

为了避免累积的往返误差, 在计算的所有地方必须一致地使用相同的色度值, 这样才能实现最大兼容性。 因此,在本规范中定义了以下两个标准的白昼模拟白点

名称 x y CCT
D50 0.345700 0.358500 5003K
D65 0.312700 0.329000 6504K

当已知颜色空间或产生颜色的设备的物理特性 (例如其使用的基色的色度, 或对一组给定输入的响应所产生的颜色)时, 我们称它为已表征

如果另外进行了调整,使设备满足校准目标,例如白点、中性色、调响应的可预测性和一致性, 那么它被称为已校准

目前的物理设备还无法产生人眼可以看到的每种可能颜色。 给定设备能够产生的颜色范围称为色域 (不要与伽马混淆)。 色域有限的设备无法产生非常饱和的颜色, 如彩虹中的颜色。

三个色域的俯视图,在 Oklab 中绘制,a 轴正方向朝右,b 轴正方向朝上;沿 l 轴向下看,因此白色和中性色位于中心。三个色域中最大的是 ITU Rec BT.2020;中等大小的是 Display P3,最小的是 sRGB。由 Alexey Ardov 渲染。

不同颜色空间的色域可以通过查看可以表示的颜色体积(以立方 Lab 单位为单位)来比较。 下表检查了 CSS 中可用的预定义颜色空间。

颜色空间 体积(百万 Lab 单位)
sRGB 0.820
display-p3 1.233
a98-rgb 1.310
prophoto-rgb 2.896
rec2020 2.042

CSS 中的颜色要么是无效颜色, 如下所述的每种语法形式, 要么是有效颜色

任何不是无效颜色的颜色都是有效颜色

某些颜色可以是有效颜色, 但仍然超出输出设备(屏幕、投影仪或打印机)可以产生的颜色范围。

这种情况称为超出色域

每个有效颜色要么对于特定输出设备(屏幕或打印机)在色域内, 要么超出色域

例如,给定一个覆盖 100% display-p3 颜色空间的屏幕, 但不超过该范围,以下颜色是超出色域的:
 color(prophoto-rgb 0.88 0.45 0.10)

因为,在 display-p3 中表示时, 一个或多个坐标要么大于 1.0,要么小于 0.0:

 color(display-p3 1.0844 0.43 0.1)

此颜色是有效的, 例如,它可以用作渐变的停止点, 但需要进行CSS 色域映射, 以便显示,从而产生看起来相似但色度较低(饱和度较低)的颜色。

3. 在 CSS 中应用颜色

3.1. 可访问性与通过颜色传达信息

测试

本节提供编写指导,不需要测试。


虽然颜色可以为文档增加重要信息并提高其可读性, 但颜色本身不应成为传达重要信息的唯一手段。 作者在文档中使用颜色时应考虑 W3C Web 内容可访问性指南 [WCAG21]

1.4.1 使用颜色: 颜色不能作为传达信息、指示操作、提示响应或区分视觉元素的唯一视觉手段

3.2. 前景颜色:color 属性

名称: color
值: <color>
初始值: CanvasText
适用于: 所有元素和文本
继承:
百分比: N/A
计算值: 计算的颜色,参见解析颜色值
规范顺序: 按语法
动画类型: 按计算值类型
测试

此属性指定元素的主要前景颜色。 它用作文本内容的填充颜色, 并且还指定了 使用的值currentcolor 将解析为该值, 这允许间接引用该前景颜色,并影响其他各种颜色属性的初始值, 如 border-colortext-emphasis-color

<color>
将主要前景颜色设置为指定的 <color>
<color> 类型提供了多种方式在语法上指定给定颜色。 例如,以下声明都指定了 sRGB 颜色 “lime”:
em { color:  lime; }   /* 颜色关键字  */
em { color:  rgb(0 255 0); } /* RGB 范围 0-255   */
em { color:  rgb(0% 100% 0%); } /* RGB 范围 0%-100% */
em { color:  color(sRGB 0 1 0); } /* sRGB 范围 0.0-1.0 */

当应用于文本时,此属性(包括其 alpha 分量)对“彩色字形”没有影响(例如某些字体中的表情符号),这些字形由内置调色板着色。 然而,一些彩色字体能够引用上下文“前景色”, 例如 OpenType 的 COLR 表中的调色板条目 0xFFFF, 或者 SVG-in-OpenType 中的 context-fill 值。 在这种情况下,前景色由此属性设置,与设置 currentcolor 值相同。

3.3. 透明度:opacity 属性

透明度可以被看作是后处理操作。 从概念上讲,在元素(包括其子元素)被渲染为 RGBA 离屏图像后, 透明度设置指定如何将离屏渲染与当前的合成渲染进行混合。 详情见简单的 alpha 合成

名称: opacity
值: <opacity-value>
初始值: 1
适用于: 所有元素
继承:
百分比: 映射到范围 [0,1]
计算值: 指定的数值,限定在 [0,1] 范围内
规范顺序: 按语法
动画类型: 按计算值类型
测试
<不透明度值>
应用于元素的透明度。 最终的透明度应用于整个元素,而不是某个特定颜色。

透明度值超出范围 [0,1] 并非无效,且在指定值中会被保留, 但在计算值中会被限定在 [0, 1] 范围内。

测试

CSS 中的不透明度使用 <opacity-value> 语法表示, 例如在 opacity 属性中。

<opacity-value> = <number> | <percentage>
        

表示为 <number> 时, 其值的有效范围是 0(表示完全透明) 到 1(表示完全不透明)。 它也可以写成 <percentage>, 其计算结果为等效的 <number>0% 对应 0100% 对应 1)。

opacity 属性将指定的不透明度应用于元素 整体, 包括其内容,而不是单独应用于每个子元素。 这意味着,例如,即使 opacity 小于 1, 遮挡部分背景的不透明子元素仍然会继续遮挡,但元素和子元素作为一个整体将显示底层页面。

这也意味着, 元素中所有字符的字形都被 作为一个整体 处理; 任何重叠部分都不会增加不透明度。

测试
重叠字形的正确与错误渲染效果
对于具有小于 1 的 opacity 值并且字形重叠的文本的正确和错误渲染。

如果希望每个字形具有不同的不透明度, 可以通过使用包含 alpha 通道的颜色值来实现, 而不是设置 opacity 属性。

如果一个盒子具有小于 1 的 opacity, 它会为其子元素形成一个堆叠上下文。 (这可以防止其内容在 z 轴上与外部内容交错。)

测试

此外,如果 z-index 属性应用于盒子, 则该元素的 auto 值被视为 0; 否则它会在其父堆叠上下文中与具有堆叠级别 0 的定位元素在同一层上绘制 (就好像它是具有 z-index:0 的定位元素一样)。

更多关于堆叠上下文的信息,请参阅 第 9.9 节附录 E,参见 [CSS2]

这些关于堆叠顺序的规则不适用于 SVG 元素, 因为 SVG 有其自己的 渲染模型[SVG11],第 3 章)。

3.4. 标记图像的颜色空间

标记图像是指显式分配了颜色配置文件的图像, 由图像格式定义。 这通常通过包含国际色彩联盟(ICC)配置文件来完成 [ICC]

例如,JPEG [JPEG]、PNG [PNG] 和 TIFF [TIFF] 都指定了一种嵌入 ICC 配置文件的方法。

图像格式也可以使用其他等效的方法,通常是为了简洁。

例如,PNG 指定了一种方法(sRGB 块), 用于显式地将图像标记为 sRGB 颜色空间, 而无需包含 sRGB ICC 配置文件。

类似地,PNG 指定了一种紧凑的方法 (cICP 块), 用于显式地将图像标记为各种 SDR 或 HDR 颜色空间之一, 例如 Display P3 或 BT.2100 HLG, 而无需包含 ICC 配置文件。

标记的 RGB 图像, 以及使用 RGB 转换(如 YCbCr)的标记图像, 如果颜色配置文件或其他识别信息有效, 必须被视为处于指定的颜色空间中。

测试

例如,当运行在具有 Display P3 显示器的系统上的浏览器显示一个标记为 ITU Rec BT.2020 [Rec.2020] 色彩空间的 JPEG 图像时,必须将颜色从 ITU Rec BT.2020 转换为 Display P3,以便正确显示。不能将 ITU Rec BT.2020 值视为 Display P3 值,否则会产生错误的颜色。

如果颜色配置文件或其他标识信息无效,则图像将按照未标记图像的描述进行处理。

3.5. 未标记颜色的色彩空间

为了兼容性,HTML 中指定的颜色 以及未标记图像必须被视为 处于 sRGB 色彩空间([SRGB]) 除非另有规定。

测试

未标记图像是指未根据图像格式定义明确指定颜色配置文件的图像。

此规则不适用于未标记视频,因为未标记视频应假定采用 ITU 定义的色彩空间。

4. 表示颜色: <color> 类型

测试

本节描述了一种类型,主要在使用该类型的地方进行测试。


CSS 中的颜色表示为颜色分量列表, 有时也称为“通道”, 代表色彩空间中的轴。 每个分量都有一个最小值和最大值, 并且可以取这两个值之间的任何值。 此外,每种颜色都附带一个 Alpha 分量, 指示其透明程度, 从而指示可以通过颜色看到多少背景。

CSS 有多种指定颜色值的语法:

颜色函数使用 CSS 函数表示法通过指定其分量坐标来表示各种色彩空间中的颜色。 其中一些使用圆柱极坐标颜色模型, 通过<hue>角度、代表亮度 (黑到白)的中心轴, 以及代表饱和度或色度 (颜色与中性灰色的距离)的半径来指定颜色。 其他的则使用矩形正交颜色模型, 使用三个正交分量轴指定颜色。

第 4 级中可用的颜色函数有:

为了方便在其他规范中参考,不透明黑色定义为颜色 rgb(0 0 0 / 100%)透明黑色是相同的颜色, 但完全透明——即 rgb(0 0 0 / 0%)

测试

4.1. <color> 语法

测试

本节提供稍后使用的定义,不需要测试。


在 CSS 中,颜色由 <color> 类型表示:

<color> = <color-base> | currentColor | <system-color>

<color-base> = <hex-color> | <color-function> | <named-color> | transparent
<color-function> = <rgb()> | <rgba()> |
              <hsl()> | <hsla()> | <hwb()> |
              <lab()> | <lch()> | <oklab()> | <oklch()> |
              <color()>

绝对颜色是一个 <color>,其计算值具有绝对的、色度学解释。这意味着该值不是:

解析为 sRGB 的颜色有:

支持旧版颜色语法的函数有:

<hsl()><hsla()><hwb()><lch()><oklch()> 颜色函数是使用 <hue> 角度的圆柱极坐标颜色表示法; 其他颜色函数使用矩形正交颜色表示法。

4.1.1. 现代(空格分隔)颜色函数语法

本规范中首次定义的所有绝对颜色函数形式 都使用现代颜色语法, 这意味着:

以下表示一个饱和的 sRGB 红色,透明度为 50%:

rgb(100% 0% 0% / 50%)

4.1.2. 传统(逗号分隔)颜色函数语法

为了 Web 兼容性, rgb()rgba()hsl()hsla() 的语法形式 (在早期规范中定义的那些) 也支持一种旧版颜色语法,它具有以下区别:

以下表示一个饱和的 sRGB 红色,透明度为 50%:

rgba(100%, 0%, 0%, 0.5)

对于本级别或随后的级别中引入的 颜色函数,在没有 Web 兼容性问题的情况下,传统颜色语法是无效的。

4.2. 表示颜色中的透明度:<alpha-value> 语法

测试

本节提供后续使用的定义,不需要测试。


<alpha-value> = <number> | <percentage>

除非另有规定,颜色的 <alpha-value> 组件在省略时默认为 100%。超出范围 [0,1] 的值不是无效的,但在解析值时会被限制在该范围内。

4.3. 表示圆柱坐标色相:<hue> 语法

测试

本节提供后续使用的定义,不需要测试。


色相表示为颜色圆的一个角度(将彩虹扭曲成一个圆,并在紫色和红色之间加入紫红色)。

<hue> = <number> | <angle>

由于这个值通常以度数给出,参数也可以用数字表示,这个数字被解释为度数,是 标准单位

这个数字被标准化为范围 [0,360)。

例如,在 hsl(-540 0 0) hsl(540 0 0) 中, <hue> 分量被标准化为 180 度。

hsl(360 0 0) 中,<hue> 分量被标准化为 0 度。

hsl(calc(-infinity) 0 0) hsl(calc(infinity) 0 0) 中, <hue> 分量再次被标准化为 0 度。

注意: 特定色相对应的角度和间距取决于色彩空间。例如,在使用 sRGB 色彩空间的 HSL 和 HWB 中,sRGB 的绿色是 120 度。在 LCH 中,sRGB 绿色是 134.39 度,display-p3 绿色是 136.01 度,a98-rgb 绿色是 145.97 度,prophoto-rgb 绿色是 141.04 度(因为这些都是不同的绿色阴影)。

<hue> 分量是最常变为无效的颜色分量; 任何足够接近中心非彩色轴的颜色 都将具有无效的色相分量。

4.4. “缺失”的颜色组件和 none 关键字

在某些情况下,颜色可能有一个或多个 缺失的颜色组件

在本规范中,由于某些颜色的 基于色相的插值,这种情况会自动发生(例如 白色);其他规范可以定义组件自动缺失的其他情况。

也可以显式指定,通过在颜色函数中为组件提供关键字 none。所有颜色函数(使用 传统颜色语法 的除外)允许其组件中的任意一个被指定为 none

这应该谨慎使用,只有在需要特定效果时才应这样做。

测试

有关在颜色插值等组合两种颜色的情况下处理缺失分量的信息, 请参阅 § 12.2 使用缺失分量进行插值

对于其他所有用途,缺失组件表现为零值,采用该组件的适当单位:00%,或0deg。这包括直接渲染颜色、将其转换为其他颜色空间、对颜色组件值执行计算等。

如果带有缺失组件的颜色被序列化或直接呈现给作者,对于传统颜色语法,其表示该组件为零值;否则,则表示该组件为none关键字。

在圆柱色彩空间中进行插值时,缺失色相是常见的。例如,使用color-mix()函数(在[CSS-COLOR-5]中定义),可以编写color-mix(in hsl, white 30%, green 70%)。由于白色是非彩色的,用hsl()表示时其色相为缺失(实际上为hsl(none 0% 100%),因为任何色相都会产生相同的颜色),这意味着color-mix函数会将其视为与绿色相同的色相(实际上为hsl(120deg 0% 100%)),然后基于这些组件进行插值。

结果将是一个真正看起来像绿色和白色混合的颜色,而不是看起来略带红色(如果将白色的色相默认为0deg)。

显式指定缺失组件对于实现只希望插值颜色的某些组件的效果非常有用。

例如,要将一种颜色动画化为“灰度”,无论该颜色是什么,可以将其与oklch(none 0 none)进行插值。这将从起始颜色中获取色相和亮度,但将其色度动画化为0,使其在整个动画中保持均匀亮度的灰色。

手动执行此操作将需要显式匹配起始颜色的色相和亮度。

4.4.1. “无效”颜色组件

个别颜色语法可以指定,在某些情况下,它们的语法中的某个组件会成为无效颜色组件。这表明该组件的值不会影响渲染的颜色;无论你赋予它什么值,屏幕上显示的颜色都是相同的。

例如,在hsl()中,当饱和度组件为0%时,色相组件是无效的0%的饱和度表示灰度颜色,它根本没有色相,因此0deg180deg或任何其他角度,都会得到完全相同的结果。

如果手动指定了无效分量, 则其行为正常; 其无效的事实没有任何影响。

但是,如果颜色是通过色彩空间转换自动生成的, 则结果中的任何无效分量都必须设置为缺失, 而不是转换过程中产生的任何值。

当执行到圆柱极坐标色彩空间的色彩空间转换时, 如果色度(或其他色彩度量,例如 hsl 中的饱和度) 小于为该色彩空间指定的 epsilon (ε),则用户代理必须将色相分量视为无效。 例如,由于数值误差, 转换为 oklch() 的灰色可能具有极小的色度,而不是精确的 0%; 因此,色相分量是无效的

4.5. 解析 <color>

测试

本节提供了一个在其他地方引用的定义,不需要测试。


解析 CSS <color>, 给定一个 字符串 input, 以及一个可选的上下文 元素 element
  1. 解析 input<color>。 如果结果是失败,则返回失败;否则,将结果赋给 color

  2. used color 设为 解析 color已使用的颜色 的结果。 如果解析 <color> 时需要元素上的其他属性值(例如解析 currentcolor系统颜色),则使用传递的 element,否则使用这些属性的初始值

  3. 返回 used color

注意:此算法并非旨在解析 CSS 样式表或 CSSOM 接口中指定的 CSS <color> 值,而是在 HTML 属性或 Canvas 接口等其他地方使用。

5. sRGB 颜色

CSS 中位于 sRGB 色彩空间内的颜色通过三元值来表示——红、绿和蓝——标识 sRGB 色彩空间中的一个点 [SRGB]。 这是一种国际公认的设备无关的色彩空间,因此适用于指定在计算机屏幕上显示的颜色,也适用于在其他类型的设备(如打印机)上指定颜色。

CSS 也允许使用非 sRGB 的 色彩空间,如 第 10 节 预定义的色彩空间 中所述。

CSS 提供了几种直接指定 sRGB 颜色的方法:十六进制颜色rgb()/rgba() 颜色函数hsl()/hsla() 颜色函数hwb() 颜色函数命名颜色, 以及 transparent 关键字。

5.1. RGB 函数:rgb()rgba()

rgb()rgba() 函数通过直接指定 r、g 和 b(红、绿、蓝)分量来定义 sRGB 颜色。 它们的语法是:

rgb() = [ <legacy-rgb-syntax> | <modern-rgb-syntax> ]
rgba() = [ <legacy-rgba-syntax> | <modern-rgba-syntax> ]
<legacy-rgb-syntax> =   rgb( <percentage>#{3} , <alpha-value>? ) |
                  rgb( <number>#{3} , <alpha-value>? )
<legacy-rgba-syntax> = rgba( <percentage>#{3} , <alpha-value>? ) |
                  rgba( <number>#{3} , <alpha-value>? )
<modern-rgb-syntax> = rgb(
  [ <number> | <percentage> | none]{3}
  [ / [<alpha-value> | none] ]?  )
<modern-rgba-syntax> = rgba(
  [ <number> | <percentage> | none]{3}
  [ / [<alpha-value> | none] ]?  )
百分比 适用于 r、g 和 b
百分比参考范围 对于 r、g 和 b:0% = 0.0,100% = 255.0
对于 alpha:0% = 0.0,100% = 1.0
测试

前三个参数分别指定颜色的 r、g 和 b(红、绿、蓝)分量。 0% 表示 sRGB 色域中该颜色分量的最小值, 100% 表示最大值。

颜色分量的百分比参考范围源于这样一个历史事实:许多图形引擎在内部将颜色分量存储为单个字节, 该字节可以容纳 0 到 255 之间的整数。 实现应尽可能遵循创作或计算的分量精度。 如果无法做到,则应将分量向 +∞ 舍入

最后一个参数 <alpha-value> 指定颜色的 Alpha 值。 如果省略,则默认为 100%

测试

超出这些范围的值并非无效, 而是在解析值时被限制在此处定义的范围内。

由于历史原因,rgb()rgba() 也支持旧版颜色语法

测试

5.2. RGB 十六进制表示法:#RRGGBB

CSS 十六进制颜色表示法允许通过将分量指定为十六进制数来指定 sRGB 颜色, 这与颜色通常在计算机代码中直接编写的方式类似。 它也比用 rgb() 表示法写出相同的颜色要短。

<hex-color> 的语法是一个 <hash-token> 标记,其值由 3、4、6 或 8 个十六进制数字组成。 换句话说,十六进制颜色写为一个哈希字符“#”, 后跟一些数字 0-9 或字母 a-f (字母的大小写无关紧要 - #00ff00#00FF00 相同)。

给定的十六进制数字的数量决定了如何将十六进制表示法解码为 RGB 颜色:

6 位数字
第一对数字,解释为十六进制数, 指定颜色的红色分量, 其中 00 表示最小值, ff(十进制的 255)表示最大值。 下一对数字,以相同的方式解释, 指定绿色分量, 最后一对指定蓝色分量。 颜色的 Alpha 分量是完全不透明的。
换句话说, #00ff00 表示与 rgb(0 255 0)(一种石灰绿)相同的颜色。
8 位数字
前 6 位数字的解释与 6 位数字表示法相同。 最后一对数字,解释为十六进制数, 指定颜色的 Alpha 分量, 其中 00 表示完全透明的颜色, ff 表示完全不透明的颜色。
换句话说, #0000ffcc 表示与 rgb(0 0 100% / 80%)(一种略微透明的蓝色)相同的颜色。
3 位数字
这是 6 位数字表示法的一种较短的变体。 第一个数字,解释为十六进制数, 指定颜色的红色分量, 其中 0 表示最小值, f 表示最大值。 接下来的两个数字分别以相同的方式表示绿色和蓝色分量。 颜色的 Alpha 分量是完全不透明的。
这种语法通常被解释为与通过“复制”所有数字获得的 6 位数字表示法相同。 例如,表示法 #123 指定与表示法 #112233 相同的颜色。 这种指定颜色的方法的“分辨率”低于 6 位数字表示法; 3 位十六进制语法中只有 4096 种可能的颜色可以表示, 而 6 位十六进制语法中大约有 1700 万种。
4 位数字
这是 8 位数字表示法的一种较短的变体, 其“扩展”方式与 3 位数字表示法相同。 第一个数字,解释为十六进制数, 指定颜色的红色分量, 其中 0 表示最小值, f 表示最大值。 接下来的三个数字分别表示绿色、蓝色和 Alpha 分量。
测试

6. 颜色关键词

除了 <color> 的各种数字语法外, CSS 还定义了几组颜色关键字,可以替代使用——每组都有其自身的优点或用例。

6.1. 命名颜色

CSS 定义了一组 命名颜色,这样常见的颜色可以更容易地书写和阅读。 一个 <named-color><ident> 的形式编写,任何需要 <color> 的地方都接受这种形式。 对于 CSS 定义的 <ident>,所有这些关键词都是 ASCII 大小写不敏感的。

这些名字解析为 sRGB 颜色。

CSS 的 16 个命名颜色最初来自 VGA 调色板,后来被 HTML 采用:aqua、black、blue、fuchsia、gray、green、lime、maroon、navy、olive、purple、red、silver、teal、white 和 yellow。 其余大部分颜色来自 X11 颜色系统的一个版本,该系统用于 Unix 衍生系统的控制台颜色指定,后来被 SVG 采用。

注意:这些颜色名称在这里被标准化,不是因为它们好,而是因为它们的使用和实现已经存在了几十年,标准需要反映现实。 实际上,很难想象每个名字对应的颜色是什么(因此有下面的列表);这些名字在 sRGB 色彩空间中的分布并不均匀,甚至它们内部也不一致( darkgray gray 更亮,而 lightpink 却比 pink 更暗), 并且一些名字(例如 indianred,最初是以来自印度的红色颜料命名的)被发现具有冒犯性。因此,不建议使用它们

(两个特殊的颜色值 transparentcurrentcolor 在各自的章节中有特殊定义。)

下表定义了所有不透明的命名颜色,通过给出其他颜色语法的等价数值表示。

命名 数值 颜色名称 十六进制 RGB 十进制
aliceblue #f0f8ff 240 248 255
antiquewhite #faebd7 250 235 215
aqua #00ffff 0 255 255
aquamarine #7fffd4 127 255 212
azure #f0ffff 240 255 255
beige #f5f5dc 245 245 220
bisque #ffe4c4 255 228 196
black #000000 0 0 0
blanchedalmond #ffebcd 255 235 205
blue #0000ff 0 0 255
blueviolet #8a2be2 138 43 226
brown #a52a2a 165 42 42
burlywood #deb887 222 184 135
cadetblue #5f9ea0 95 158 160
chartreuse #7fff00 127 255 0
chocolate #d2691e 210 105 30
coral #ff7f50 255 127 80
cornflowerblue #6495ed 100 149 237
cornsilk #fff8dc 255 248 220
crimson #dc143c 220 20 60
cyan #00ffff 0 255 255
darkblue #00008b 0 0 139
darkcyan #008b8b 0 139 139
darkgoldenrod #b8860b 184 134 11
darkgray #a9a9a9 169 169 169
darkgreen #006400 0 100 0
darkgrey #a9a9a9 169 169 169
darkkhaki #bdb76b 189 183 107
darkmagenta #8b008b 139 0 139
darkolivegreen #556b2f 85 107 47
darkorange #ff8c00 255 140 0
darkorchid #9932cc 153 50 204
darkred #8b0000 139 0 0
darksalmon #e9967a 233 150 122
darkseagreen #8fbc8f 143 188 143
darkslateblue #483d8b 72 61 139
darkslategray #2f4f4f 47 79 79
darkslategrey #2f4f4f 47 79 79
darkturquoise #00ced1 0 206 209
darkviolet #9400d3 148 0 211
deeppink #ff1493 255 20 147
deepskyblue #00bfff 0 191 255
dimgray #696969 105 105 105
dimgrey #696969 105 105 105
dodgerblue #1e90ff 30 144 255
firebrick #b22222 178 34 34
floralwhite #fffaf0 255 250 240
forestgreen #228b22 34 139 34
fuchsia #ff00ff 255 0 255
gainsboro #dcdcdc 220 220 220
ghostwhite #f8f8ff 248 248 255
gold #ffd700 255 215 0
goldenrod #daa520 218 165 32
gray #808080 128 128 128
green #008000 0 128 0
greenyellow #adff2f 173 255 47
grey #808080 128 128 128
honeydew #f0fff0 240 255 240
hotpink #ff69b4 255 105 180
indianred #cd5c5c 205 92 92
indigo #4b0082 75 0 130
ivory #fffff0 255 255 240
khaki #f0e68c 240 230 140
lavender #e6e6fa 230 230 250
lavenderblush #fff0f5 255 240 245
lawngreen #7cfc00 124 252 0
lemonchiffon #fffacd 255 250 205
lightblue #add8e6 173 216 230
lightcoral #f08080 240 128 128
lightcyan #e0ffff 224 255 255
lightgoldenrodyellow #fafad2 250 250 210
lightgray #d3d3d3 211 211 211
lightgreen #90ee90 144 238 144
lightgrey #d3d3d3 211 211 211
lightpink #ffb6c1 255 182 193
lightsalmon #ffa07a 255 160 122
lightseagreen #20b2aa 32 178 170
lightskyblue #87cefa 135 206 250
lightslategray #778899 119 136 153
lightslategrey #778899 119 136 153
lightsteelblue #b0c4de 176 196 222
lightyellow #ffffe0 255 255 224
lime #00ff00 0 255 0
limegreen #32cd32 50 205 50
linen #faf0e6 250 240 230
magenta #ff00ff 255 0 255
maroon #800000 128 0 0
mediumaquamarine #66cdaa 102 205 170
mediumblue #0000cd 0 0 205
mediumorchid #ba55d3 186 85 211
mediumpurple #9370db 147 112 219
mediumseagreen #3cb371 60 179 113
mediumslateblue #7b68ee 123 104 238
mediumspringgreen #00fa9a 0 250 154
mediumturquoise #48d1cc 72 209 204
mediumvioletred #c71585 199 21 133
midnightblue #191970 25 25 112
mintcream #f5fffa 245 255 250
mistyrose #ffe4e1 255 228 225
moccasin #ffe4b5 255 228 181
navajowhite #ffdead 255 222 173
navy #000080 0 0 128
oldlace #fdf5e6 253 245 230
olive #808000 128 128 0
olivedrab #6b8e23 107 142 35
orange #ffa500 255 165 0
orangered #ff4500 255 69 0
orchid #da70d6 218 112 214
palegoldenrod #eee8aa 238 232 170
palegreen #98fb98 152 251 152
paleturquoise #afeeee 175 238 238
palevioletred #db7093 219 112 147
papayawhip #ffefd5 255 239 213
peachpuff #ffdab9 255 218 185
peru #cd853f 205 133 63
pink #ffc0cb 255 192 203
plum #dda0dd 221 160 221
powderblue #b0e0e6 176 224 230
purple #800080 128 0 128
rebeccapurple #663399 102 51 153
red #ff0000 255 0 0
rosybrown #bc8f8f 188 143 143
royalblue #4169e1 65 105 225
saddlebrown #8b4513 139 69 19
salmon #fa8072 250 128 114
sandybrown #f4a460 244 164 96
seagreen #2e8b57 46 139 87
seashell #fff5ee 255 245 238
sienna #a0522d 160 82 45
silver #c0c0c0 192 192 192
skyblue #87ceeb 135 206 235
slateblue #6a5acd 106 90 205
slategray #708090 112 128 144
slategrey #708090 112 128 144
snow #fffafa 255 250 250
springgreen #00ff7f 0 255 127
steelblue #4682b4 70 130 180
tan #d2b48c 210 180 140
teal #008080 0 128 128
thistle #d8bfd8 216 191 216
tomato #ff6347 255 99 71
turquoise #40e0d0 64 224 208
violet #ee82ee 238 130 238
wheat #f5deb3 245 222 179
white #ffffff 255 255 255
whitesmoke #f5f5f5 245 245 245
yellow #ffff00 255 255 0
yellowgreen #9acd32 154 205 50

注意:此颜色列表及其定义是 SVG 1.1 定义的命名颜色 列表的超集。

为了历史原因,这也被称为 X11 颜色集。

注意: X11 颜色系统的历史非常有趣,并由 Alex Sexton 在其演讲 “Peachpuffs and Lemonchiffons” 中做了极好的总结

测试

6.2. 系统颜色

通常,<系统颜色>关键字反映了用户、浏览器或操作系统做出的默认颜色选择。因此,它们通常在浏览器的默认样式表中使用。

为了保持可读性,<系统颜色>关键字也会响应亮模式或暗模式的变化。

然而,在强制颜色模式下,页面上的大多数颜色会被强制转换为用户选择的受限调色板。<系统颜色>关键字会暴露这些用户选择的颜色,以便页面的其他部分能够与受限的调色板融合。

强制颜色 媒体特性 激活时,作者应该使用<系统颜色>关键字作为属性中的颜色值,这些属性除外,请参考CSS 颜色调整 1 § 3.1 被强制颜色模式影响的属性,以确保页面的可读性和一致性,避免用户强制颜色和页面选择颜色的混乱拼凑。

测试

<系统颜色>关键字的值来自浏览器(而不是作为操作系统的默认值或用户选择)时,浏览器应确保匹配的前景/背景对具有最小的WCAG AA对比度。然而,用户偏好(无论是更高还是更低的对比度),无论是作为浏览器偏好设置、用户样式表设置,还是通过更改操作系统的默认设置,必须优先于此要求。

作者也可以随时使用这些关键字,但应该小心使用匹配的背景-前景对来确保适当的对比度,因为跨不匹配对的特定对比关系(例如,CanvasButtonText)不保证。

<系统颜色>关键字定义如下:

AccentColor
突出显示的用户界面控件的背景。
AccentColorText
突出显示的用户界面控件的文本。
ActiveText
活动链接中的文本。对于浅色背景,通常为红色。
ButtonBorder
按钮的边框颜色。
ButtonFace
按钮的表面背景颜色。
ButtonText
按钮上的文本。
Canvas
应用程序内容或文档的背景。
CanvasText
应用程序内容或文档中的文本。
Field
输入字段的背景。
FieldText
输入字段中的文本。
GrayText
禁用文本。(通常,但不一定是灰色。)
Highlight
选中文本的背景,例如来自::selection。
HighlightText
选中文本的文本。
LinkText
非活动、未访问链接中的文本。对于浅色背景,通常为蓝色。
Mark
被特别标记的文本的背景(例如通过HTML mark 元素)。
MarkText
被特别标记的文本(例如通过HTML mark 元素)。
SelectedItem
选中项的背景,例如选中的复选框。
SelectedItemText
选中项的文本。
VisitedText
已访问链接中的文本。对于浅色背景,通常为紫色。
测试

注意:与所有其他关键字一样,这些名称是ASCII 大小写不敏感的。它们在这里以混合大小写显示以便于阅读。

对于没有特定系统 UI 概念的系统,指定的值应映射到最接近的相关系统颜色值。以下系统颜色配对预计将形成可读的背景-前景颜色:

此外,GrayText预计可以阅读,尽管对比度可能较低,在任何背景上都可以阅读。

例如,您当前使用的浏览器中的系统颜色组合:

Canvas 与 CanvasText:CanvasText

Canvas 与 LinkText:LinkText

Canvas 与 VisitedText:VisitedText

Canvas 与 ActiveText:ActiveText

Canvas 与 GrayText:GrayText

Canvas 与 ButtonBorder 以及相邻的 Canvas:CanvasText相邻

ButtonFace 与 ButtonText:ButtonText

ButtonFace 与 ButtonText 和 ButtonBorder:ButtonText

ButtonFace 与 GrayText:GrayText

Field 与 FieldText:FieldText

Field 与 GrayText:GrayText

Mark 与 MarkText:MarkText

Mark 与 GrayText:GrayText

Highlight 与 HighlightText:HighlightText

Highlight 与 GrayText:GrayText

SelectedItem 与 SelectedItemText:SelectedItemText

AccentColor 与 AccentColorText:AccentColorText

AccentColor 与 GrayText:GrayText

早期版本的CSS定义了其他<系统颜色>,这些已经被弃用。它们在附录A:弃用的CSS系统颜色中进行了记录。

注意:<系统颜色>存在一些隐私和安全风险,详见§ 21 隐私考虑§ 20 安全考虑

用户代理可以选择返回固定值以减轻隐私和安全风险(例如指纹识别),这些值不反映用户的自定义或主题选择。

6.3. transparent关键字

关键字transparent指定了透明黑。它是一种<named-color>

测试

6.4. currentcolor关键字

关键字currentcolor表示同一元素上的color属性的值。与<named-color>不同,它局限于 sRGB;该值可以是任何<color>。其使用值解析颜色值来确定。

测试
这是一个展示如何使用currentcolor关键字的简单示例:
.foo {
  color:  red;
  background-color:  currentcolor;
}

这相当于写成:

.foo {
  color:  red;
  background-color:  red;
}
例如,text-emphasis-color属性[CSS3-TEXT-DECOR]的初始值为currentcolor,默认情况下匹配文本颜色,即使color属性在不同元素中变化。
<p><em>Some <strong>really</strong> emphasized text.</em> 
<style>
p { color: black; }
em { text-emphasis: dot; }
strong { color: red; }
</style>

呈现了带有红色加重点的文本中强调的'really'部分

在上面的示例中,强调标记在文本 "Some" 和 "emphasized text" 上为黑色,但在文本 "really" 上为红色。

注意: 在 CSS 中,多词关键字通常用连字符分隔其组成单词。currentcolor没有这样做,因为(深呼吸)它最初在 SVG 中作为属性值 "current-color" 被引入,遵循通常的 CSS 拼写。随后,它(连同所有其他属性及其值)成为表现属性和属性值,以及属性,以使得使用 XSLT 的生成更容易。然后所有表现属性从连字符形式变为 camelCase,因为 DOM 对连字符表示“减号”有问题。但这样一来,它们就不再遵循 CSS 约定了,因此所有已经属于 CSS 的属性和属性值都改回了连字符形式!currentcolor当时并不属于 CSS,因此保留了 camelCase。后来 CSS 接受了它,此时由于 CSS 关键字是ASCII 大小写不敏感的,所以拼写方式不再重要。

7. HSL 颜色:hsl()hsla() 函数

RGB 系统用于指定颜色, 对于机器和图形库来说很方便, 但通常被认为对人类来说很难直观地理解。 例如,很难知道如何改变 RGB 颜色以产生同一色调的较亮变体。

还有几种其他的颜色方案可行。 其中一种是 HSL [HSL] 颜色方案, 使用起来更直观, 但仍可以轻松映射回 RGB 颜色。

HSL 颜色是通过色相、饱和度和亮度三元组来指定的。 hsl()hsla() 函数的语法是:

hsl() = [ <legacy-hsl-syntax> | <modern-hsl-syntax> ]
hsla() = [ <legacy-hsla-syntax> | <modern-hsla-syntax> ]
<modern-hsl-syntax> = hsl(
    [<hue> | none]
    [<percentage> | <number> | none]
    [<percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
<modern-hsla-syntax> = hsla(
    [<hue> | none]
    [<percentage> | <number> | none]
    [<percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
<legacy-hsl-syntax> = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
<legacy-hsla-syntax> = hsla( <hue>, <percentage>, <percentage>, <alpha-value>? )
百分比 适用于 S 和 L
百分比参考范围 对于 S 和 L:0% = 0.0,100% = 100.0
无效色相 ε S <= 0.001
测试

第一个参数指定色相角度。

在 HSL(和 HWB)中,角度 0deg 代表 sRGB 原始红色(同样适用于 360deg720deg 等),其余的色相围绕圆周分布,所以 120deg 代表 sRGB 原始绿色,240deg 代表 sRGB 原始蓝色,依此类推。

接下来的两个参数分别是饱和度和亮度。对于饱和度,100%100 表示完全饱和、明亮的颜色,而 0%0 表示完全不饱和的灰色。对于亮度,50%50 表示“正常”颜色,而 100%100 是白色,0%0 是黑色。

由于历史原因,如果饱和度小于 0%,它将在解析值阶段被钳制到 0%,然后再转换为 sRGB 颜色。

测试

最后一个参数指定颜色的 Alpha 分量。 它的解释与 rgb() 函数的第四个参数完全相同。 如果省略,则默认为 100%

HSL 颜色解析为 sRGB。

如果 HSL 颜色的饱和度为 0%0,则色相分量是无效的

例如,一个普通的红色,即使用关键字 red 或十六进制表示法 #f00 的颜色,可以在 HSL 中表示为 hsl(0deg 100% 50%)

与 RGB 相比,HSL 的一个优点是它更直观:人们可以猜测他们想要的颜色,然后进行调整。

例如,以下颜色都可以基于基本的“绿色”色相,通过仅改变其他两个参数生成:
hsl(120deg 100% 50%) 亮绿色
hsl(120deg 100% 25%) 深绿色
hsl(120deg 100% 75%) 浅绿色
hsl(120deg 75% 85%) 粉绿色

HSL 相对于 OKLCh 的一个缺点是,色相操作会改变视觉亮度,并且色相并非均匀分布。

因此,在 HSL 中创建一组匹配的颜色(通过保持色相不变并改变饱和度和亮度)比操作 sRGB 组件值更容易;但是,由于亮度只是伽马校正的红、绿和蓝组件的平均值,因此它不对应于色相的视觉亮度。

例如, 蓝色 在 HSL 中表示为 hsl(240deg 100% 50%),而 黄色 则为 hsl(60deg 100% 50%)。两者的 HSL 亮度均为 50%,但明显黄色看起来比蓝色亮得多。

在 OKLCh 中,sRGB 蓝色为 oklch(0.452 0.313 264.1),而 sRGB 黄色为 oklch(0.968 0.211 109.8)。OKLCh 的亮度值 0.452 和 0.968 明确地反映了两种颜色的视觉亮度。

在 HSL 中,色相角度并非感知上均匀的;颜色在某些区域聚集,而在其他区域则较为稀疏。

例如,色相对为 hsl(220deg 100% 50%) hsl(250deg 100% 50%) 的颜色具有 250-220 = 30度的 HSL 色相差异,看起来相当相似,而另一对颜色 hsl(50deg 100% 50%) hsl(80deg 100% 50%),其色相差异也是 80-50 = 30度,但看起来却非常不同。

在 OKLCh 中,相同的颜色对 oklch(0.533 0.26 262.6) oklch(0.462 0.306 268.9) 的色相差异为 268.9 - 262.6 = 6.3度,而另一对 oklch(0.882 0.181 94.24) oklch(0.91 0.245 129.9) 的色相差异为 129.9 - 94.24 = 35.66度,正确地反映了色相的视觉分离程度。

由于历史原因,hsl()hsla() 也支持旧版颜色语法

测试

7.1. 将 HSL 颜色转换为 sRGB

将 HSL 颜色转换为 sRGB 在数学上很简单。 以下是 JavaScript 中转换算法的示例实现。 它返回一个包含三个数字的数组, 分别表示颜色的红色、绿色和蓝色分量, 对于 sRGB 色域中的颜色,这些分量将在 [0, 1] 范围内。

此代码假设已经应用了解析时对负饱和度的限制。

/**
 * @param {number} hue - Hue as degrees 0..360
 * @param {number} sat - Saturation in reference range [0,100]
 * @param {number} light - Lightness in reference range [0,100]
 * @return {number[]} Array of sRGB components; in-gamut colors in range [0..1]
 */
function hslToRgb(hue, sat, light) {

    sat /= 100;
    light /= 100;

    function f(n) {
        let k = (n + hue/30) % 12;
        let a = sat * Math.min(light, 1 - light);
        return light - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
    }

    return [f(0), f(8), f(4)];
}

7.2. 将 sRGB 颜色转换为 HSL

反向的转换过程类似。

特别注意处理由远离 sRGB 色域的颜色产生的中间负饱和度值。

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number[]} Array of HSL values: Hue as degrees 0..360, Saturation and Lightness in reference range [0,100]
 */
function rgbToHsl (red, green, blue) {
    let max = Math.max(red, green, blue);
    let min = Math.min(red, green, blue);
    let [hue, sat, light] = [NaN, 0, (min + max)/2];
    let d = max - min;
    let epsilon = 1 / 100000;   // max Sat is 1, in this code

    if (d !== 0) {
        sat = (light === 0 || light === 1)
            ? 0
            : (max - light) / Math.min(light, 1 - light);

        switch (max) {
            case red:   hue = (green - blue) / d + (green < blue ? 6 : 0); break;
            case green: hue = (blue - red) / d + 2; break;
            case blue:  hue = (red - green) / d + 4;
        }

        hue = hue * 60;
    }

    // Very out of gamut colors can produce negative saturation
    // If so, just rotate the hue by 180 and use a positive saturation
    // see https://github.com/w3c/csswg-drafts/issues/9222
    if (sat < 0) {
        hue += 180;
        sat = Math.abs(sat);
    }

    if (hue >= 360) {
        hue -= 360;
    }

    if (sat <= epsilon) {
        hue = NaN;
    }

    return [hue, sat * 100, light * 100];
}

7.3. HSL 颜色示例

本节不是规范性的。

测试

本节不是规范性的,因此不需要测试。


下表展示了可能的 HSL 颜色的广泛范围。 每个表格代表一个色相, 以 30° 为间隔选择, 以说明常见的 "核心" 色调: 红色, 黄色, 绿色, 青色, 蓝色, 洋红色, 以及这些之间的六种中间颜色。

在每个表格中,X 轴表示饱和度, Y 轴表示亮度。

0° 红色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
30° 红-黄(=橙色)
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
60° 黄色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
90° 黄绿色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
120° 绿色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
150° 绿青色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
180° 青色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
210° 青-蓝色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
240° 蓝色
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
270° 蓝-洋红
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
300° 洋红
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%
330° 洋红-红
100% 80% 60% 40% 20% 0%
100%
90%
80%
70%
60%
50%
40%
30%
20%
10%
0%

8. HWB 颜色:hwb() 函数

HWB(色相-白度-黑度的缩写)[HWB] 是另一种指定 sRGB 颜色的方法,类似于HSL,但通常对人类来说更容易使用。它通过一个起始色相,然后混入一定程度的白色和黑色来描述颜色。

许多颜色选择器基于 HWB 色彩系统,因为它直观易用。

HWB 颜色解析为 sRGB。

这是 Chrome 的颜色选择器的截图,当用户激活 <input type="color"> 时显示。外部的轮盘用于选择色相,然后通过单击内部三角形选择相对的白色和黑色量。

hwb() 函数的语法如下:

hwb() = hwb(
  [<hue> | none]
  [<percentage> | <number> | none]
  [<percentage> | <number> | none]
  [ / [<alpha-value> | none] ]? )
百分比 允许用于 W 和 B
百分比参考范围 对于 W 和 B:0% = 0.0,100% = 100.0
无效色相 ε W + B >= 99.999

第一个参数指定色相, 其定义与 hsl() 完全相同; 这意味着它具有相同的缺点,例如色相均匀性。

第二个参数指定要混合的白色量, 百分比范围从 0%(无白色)到 100%(全白色)。 类似地,第三个参数指定要混合的黑色量, 百分比范围也从 0%(无黑色)到 100%(全黑色)。

例如, hwb(150 20% 10%) 与 hsl(150 77.78% 55%) 以及 rgb(20% 90% 55%) 为同一种颜色。

超出这些范围的值并不无效;色相角度超出 [0,360) 范围将被归一化至该范围,而白色和黑色的值相加为 100% 或更高时,将生成如下所述的无彩色。

最终的颜色可以在概念上理解为选择的色相、白色颜料和黑色颜料的混合物,相对的数量由百分比决定。

如果白色加黑色的和大于或等于 100%,则定义为无彩色,即灰色阴影;转换为 sRGB 时,R、G 和 B 的值相等,其值为 white / (white + black)。

例如,颜色 hwb(45 40% 80%) 中,白色和黑色的和为 120,因此这是一个无彩色,其 R、G 和 B 组件为 40 / (40 + 80) = 0.33, rgb(33.33% 33.33% 33.33%)。

无彩色的 HWB 颜色不再包含任何所选择的色相。在这种情况下,色相组件为无效

第四个参数指定颜色的 Alpha 分量。 它的解释与 rgb() 函数的第四个参数完全相同。 如果省略,则默认为 100%

由于 hwb 是本规范此级别中的新增内容,因此不存在 Web 兼容性问题,所以 hwb() 支持使用逗号分隔其所有参数的旧版颜色语法。 在 hwb() 内部使用逗号是错误的。

测试

8.1. 将 HWB 颜色转换为 sRGB

将 HWB 颜色转换为 sRGB 非常简单,类似于将 HSL 转换为 RGB 的方式。以下是算法的 Javascript 实现,首先将白色和黑色组件标准化,使它们的总和不超过 100%。

/**
 * @param {number} hue -  Hue as degrees 0..360
 * @param {number} white -  Whiteness in reference range [0,100]
 * @param {number} black -  Blackness in reference range [0,100]
 * @return {number[]} Array of RGB components 0..1
 */
function hwbToRgb(hue, white, black) {
    white /= 100;
    black /= 100;
    if (white + black >= 1) {
        let gray = white / (white + black);
        return [gray, gray, gray];
    }
    let rgb = hslToRgb(hue, 100, 50);
    for (let i = 0; i < 3; i++) {
        rgb[i] *= (1 - white - black);
        rgb[i] += white;
    }
    return rgb;
}

8.2. 将 sRGB 颜色转换为 HWB

反向转换的过程类似。

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number} Hue as degrees 0..360
 */
function rgbToHue(red, green, blue) {
    // Similar to rgbToHsl, except that saturation and lightness are not calculated, and
    // potential negative saturation is ignored.
    let max = Math.max(red, green, blue);
    let min = Math.min(red, green, blue);
    let hue = NaN;
    let d = max - min;

    if (d !== 0) {
        switch (max) {
            case red:   hue = (green - blue) / d + (green < blue ? 6 : 0); break;
            case green: hue = (blue - red) / d + 2; break;
            case blue:  hue = (red - green) / d + 4;
        }

        hue *= 60;
    }

    if (hue >= 360) {
        hue -= 360;
    }

    return hue;
}

/**
 * @param {number} red - Red component 0..1
 * @param {number} green - Green component 0..1
 * @param {number} blue - Blue component 0..1
 * @return {number[]} Array of HWB values: Hue as degrees 0..360, Whiteness and Blackness in reference range [0,100]
 */
function rgbToHwb(red, green, blue) {
    let epsilon = 1 / 100000;  // account for multiply by 100
    var hue = rgbToHue(red, green, blue);
    var white = Math.min(red, green, blue);
    var black = 1 - Math.max(red, green, blue);
    if (white + black >= 1 - epsilon) {
        hue = NaN;
    }
    return([hue, white*100, black*100]);
}

8.3. HWB 颜色示例

本节不是规范性内容。

测试

本节不是规范性内容,不需要测试。


0° 红色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
30° 红-黄色(橙色)
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
60° 黄色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
90° 黄-绿色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
120° 绿色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
150° 绿-青色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
180° 青色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
210° 青-蓝色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
240° 蓝色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
270° 蓝-品红色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
300° 品红色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%
330° 品红-红色
W\B 0% 20% 40% 60% 80% 100%
0%
20%
40%
60%
80%
100%

9. 设备无关颜色:CIE Lab 和 LCH,Oklab 和 OKLCh

9.1. CIE Lab 和 LCH

本节不具规范性。

测试

本节不具规范性,不需要测试。


颜色的物理测量通常表示为 CIE L*a*b* [CIELAB] 色彩空间, 该空间由 CIE 于 1976 年创建,通常简称为 Lab。 从一个设备转换到另一个设备的颜色也可以使用 Lab 作为中间步骤。 Lab 是基于人类视觉实验得出的, 代表了人类可见的所有颜色范围。

Lab 是一个具有中央亮度(L)轴的直角坐标系统。 该值通常表示为无单位的数字; 为了与 CSS 其他部分兼容,也可以写为百分比。 100% 表示 L 值为 100,而不是 1.0。 L=0% 或 0 是深黑色(完全没有光) 而 L=100% 或 100 是漫反射白。

有用的是,L=50% 或 50 是中灰色,且设计上, L 的等增量在视觉上是均匀间隔的: Lab 色彩空间旨在实现感知上的均匀性

此图左侧显示了 CIE Lab 色彩空间的亮度轴。 显示了二十一个中性色块(L=0%,L=5%,一直到 L=100%)。 这些色阶在视觉上是等距的。 右侧,亮度中相同数量的色阶在光能上是等距的,但在视觉上不等距

a 和 b 轴传递色相; a 轴正值表示紫红色, 而负值则是互补色,绿色。 类似地,b 轴正值表示黄色, 负值表示互补色蓝色/紫色。 去饱和的颜色具有较小的 a 和 b 值, 并靠近 L 轴; 饱和的颜色则远离 L 轴。

光源是 D50 白光,这是一种标准化的日光光谱,色温为 5000K, 由完美漫反射体反射;它近似于晴天阳光的颜色。 D50 也是 ICC 颜色相互转换中配置文件连接空间使用的白点, 是提供 Lab 编辑功能的图像编辑器中使用的白点, 也是物理测量设备(如分光光度计和分光辐射计) 在报告 Lab 测量颜色时使用的值。

使用其他白点指定颜色的转换被称为 色度适应变换, 其模拟了人类视觉系统在适应新的照明条件时的变化。 线性布拉德福德算法 [ICC](原布拉德福德算法的简化版 [Bradford-CAT]) 是行业标准的色度适应变换, 计算非常简单,因为它只是一个矩阵乘法。

CIE LCH 与 Lab 具有相同的 L 轴, 但使用极坐标 C(色度)和 H(色相), 形成了一个极坐标的柱面坐标系统。 C 是从 L 轴的几何距离, H 是从 a 轴正向到 b 轴正向的角度。

图中显示了 CIE Lab 色彩空间的 L=50 平面。 每 20 度的增量以圆形显示, 分别具有三个色度等级:20、40 和 60。 所有 20 色度的颜色均可包含在 sRGB 色域内, 部分 40 和 60 色度的颜色则在 sRGB 色域之外。 这些色域外的颜色以灰色可视化,并带有红色警告边框。

注:Lab 和 LCH 的 L 轴 不要与 HSL 的 L 轴混淆。 例如,在 HSL 中,sRGB 颜色蓝色(#00F)和黄色(#FF0) 具有相同的 L 值(50%),即使在视觉上,蓝色要暗得多。 在 Lab 中这就非常清晰: sRGB 蓝色为 lab(29.567% 68.298 -112.0294) 而 sRGB 黄色为 lab(97.607% -15.753 93.388)。 在 Lab 和 LCH 中,如果两个颜色具有相同的 L 值, 那么它们在视觉亮度上是相同的。 HSL 和相关的极坐标 RGB 模型是为了试图 提供类似于 LCH 给 Lab 的可用性好处的 RGB, 但其准确性显著低于 Lab 和 LCH。

尽管 CIE Lab 和 LCH 得到了广泛使用, 但它们也存在一些问题,尤其是:

色相线性度
在蓝色区域(LCH 色相介于 270° 和 330° 之间), 视觉色相偏离了 LCH 的预测。 如果绘制一组相同色相且色度不同的蓝色, 这些颜色应该形成一条从中性轴延伸的直线, 但实际上形成了曲线。 换句话说, 随着一个饱和的蓝色的色度逐渐减少, 它会变得显著偏紫。
色相均匀性
虽然 LCH 中的色相通常是均匀间隔的, (且远优于 HSL 或 HWB), 但均匀性并不完美。
高色度差异的过度预测
对于高色度的颜色, 色度的变化不如中性色更明显。

这些缺陷影响了,例如, 创建均匀间隔的渐变, 将一个色彩空间映射到一个较小的色彩空间, 以及计算两个颜色之间的视觉差异。

为弥补这一点, 预测两个颜色之间视觉差异的公式 (Delta E) 随时间的推移变得更加准确 (但也更复杂)。 当前的行业标准公式, Delta E 2000, 对于缓解 Lab 和 LCH 的某些问题效果良好。 § 19.1 ΔE2000 中给出了示例实现。

但是这对色相曲率问题没有帮助。

9.2. Oklab 和 OKLCh

本节不具规范性。

测试

本节不具规范性,不需要测试。


最近,Oklab,一种改进的类 Lab 空间被开发出来 [Oklab]。 对应的极坐标形式称为 OKLCh。 它是通过对大量视觉上相似的颜色数据集进行数值优化而产生的, 与 CIE LCH 相比,其色相线性度、色相均匀性和色度均匀性都有所提高。

与 CIE Lab 一样,Oklab 有一个中心的亮度 L 轴, 通常表示为范围 [0,1] 的无单位数值; 为了与 CSS 其他部分兼容, 也可以写为百分比。100% 表示 L 值为 1.0。 L=0% 或 0.0 表示深黑色(完全没有光),而 L=100% 或 1.0 表示漫反射白。

注: 与 CIE Lab 不同,CIE Lab 假设适应漫反射白色, 而 Oklab 假设适应被定义的颜色, 这旨在使其具有比例不变性。

与 CIE Lab 一样,a 和 b 轴传递色相; a 轴正值表示紫红色, 负值表示互补色,绿色。 类似地,b 轴正值表示黄色, 负值表示互补的蓝色/紫色。

光源是 D65,与大多数 RGB 色彩空间使用的白点相同。

OKLCh 与 Oklab 具有相同的 L 轴, 但使用极坐标 C(色度)和 H(色相)。

注意:与 CIE LCH 中色度可以达到 200 或更高不同, OKLCh 色度范围约为 0.5。 CIE LCH 和 OKLCh 之间的色相角大致相似, 但不完全相同。

CIE LCH 中的紫化现象图
恒定的 CIE LCH 色相切片, 显示了 sRGB 色域中的主要蓝色区域。 可以明显看到一种紫化现象。
diagram showing hue constancy in OKLCh
恒定的 OKLCh 色相切片, 显示原蓝色周围的 sRGB 色域。 视觉色相保持不变。

由于 Oklab 的感知均匀性优于 CIE Lab, 因此颜色差异可以通过 3D 空间中的距离(平方和的平方根)直接计算。 尽管计算简单, § 19.2 ΔEOK 中给出了示例实现。

9.3. 指定 Lab 和 LCH:lab()lch() 函数表示法

CSS 允许直接以 Lab 和 LCH 表示颜色。

lab() = lab( [<percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ / [<alpha-value> | none] ]? )
百分比 允许用于 L, a 和 b
百分比参考范围 对于 L:0% = 0.0, 100% = 100.0
对于 a 和 b:-100% = -125, 100% = 125
测试

Lab中, 第一个参数指定了 CIE 亮度,即 L。 这是一个介于 0% 或 0 和 100% 或 100 之间的数值。 值小于 0% 或 0 时必须在解析值时夹紧为 0%; 值大于 100% 或 100 时在解析值时夹紧为 100%

第二和第三个参数是 Lab 色彩空间中沿 "a" 和 "b" 轴的距离, 如上一节所述。 这些值是带符号的 (允许正值和负值), 理论上是无界的 (但实际上不超过 ±160)。

还有一个可选的第四个<alpha-value> 组件, 通过斜杠分隔, 代表alpha 组件

如果 Lab 颜色的亮度(经过夹紧后)为 0%, 或 100%,由于色域映射到显示器,该颜色将分别显示为黑色或白色。

 lab(29.2345% 39.3825 20.0664);
 lab(52.2345 40.1645 59.9971);
 lab(60.2345 -5.3654 58.956);
 lab(62.2345% -34.9638 47.7721);
 lab(67.5345 -8.6911 -41.6019);
 lab(29.69% 44.888% -29.04%)
lch() = lch( [<percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ <hue> | none]
      [ / [<alpha-value> | none] ]? )
百分比 允许用于 L 和 C
百分比参考范围 对于 L: 0% = 0.0, 100% = 100.0
对于 C: 0% = 0, 100% = 150
无效色相 ε C <= 0.0015
测试

在 CIE LCH 中,第一个参数表示 CIE 的亮度 L,与 lab() 的亮度参数相同。

第二个参数是色度 C(大致表示“颜色的浓度”)。 它的最小有效值为 0,而最大值在理论上是无限的(但实际上不超过 230)。 如果提供的值为负数,则在解析值阶段将其夹紧到 0

第三个参数是色相角 H。 它的解释方式类似于 <hue> 参数在 hsl() 中的解释方式, 但在这里色相均匀地分布在角度上,而不是与角度相同的映射。 因此,0deg 沿正 "a" 轴方向(朝向紫红色),(同样适用于 360deg720deg 等); 90deg 沿正 "b" 轴方向(朝向芥黄色),180deg 沿负 "a" 轴方向 (朝向绿色青色),270deg 沿负 "b" 轴方向(朝向天蓝色)。

有一个可选的第四个 <alpha-value> 组件, 用斜线分隔,表示 alpha 组件

如果 LCH 颜色的色度为 0%,则色相组件是 无效的。 如果 LCH 颜色的亮度(在夹紧后)为 0%100%,则颜色将分别显示为黑色或白色, 这是由于显示器的色域映射。

 lch(29.2345% 44.2 27);
 lch(52.2345% 72.2 56.2);
 lch(60.2345 59.2 95.2);
 lch(62.2345% 59.2 126.2);
 lch(67.5345% 42.5 258.2);
 lch(29.69% 45.553% 327.1)

lablch 中, 这些函数在此规范级别是全新的, 因此lab()lch() 支持使用逗号分隔其所有参数的 旧颜色语法。 在这些函数中使用逗号是错误的。

9.4. 指定 Oklab 和 OKLCh:oklab()oklch() 函数表示法

CSS 允许直接用 Oklab 和 OKLCh 表示颜色。

oklab() = oklab( [ <percentage> | <number> | none]
    [ <percentage> | <number> | none]
    [ <percentage> | <number> | none]
    [ / [<alpha-value> | none] ]? )
百分比 L、a 和 b 可以使用百分比
百分比参考范围 对于 L:0% = 0.0,100% = 1.0
对于 a 和 b:-100% = -0.4,100% = 0.4
测试

Oklab 中,第一个参数指定 Oklab 的亮度。 这是一个在 0% 或 0 和 100% 或 1.0 之间的数字。

小于 0% 或 0.0 的值必须在解析时被钳制到 0%; 大于 100% 或 1.0 的值在解析时被钳制到 100%

第二个和第三个参数是沿 Oklab 颜色空间中的 "a" 和 "b" 轴的距离, 如前一节所述。 这些值是有符号的 (允许正值和负值) 理论上是无限的 (但实际上不超过 ±0.5)。

还有一个可选的第四个 <alpha-value> 组件, 用斜杠分隔, 表示alpha 组件

如果 Oklab 颜色的亮度为 0% 或 0, 或者 100% 或 1.0, 则由于显示的色域映射, 颜色将分别显示为黑色或白色。

 oklab(40.101% 0.1147 0.0453);
 oklab(59.686% 0.1009 0.1192);
 oklab(0.65125 -0.0320 0.1274);
 oklab(66.016% -0.1084 0.1114);
 oklab(72.322% -0.0465 -0.1150);
 oklab(42.1% 41% -25%)
oklch() = oklch( [ <percentage> | <number> | none]
      [ <percentage> | <number> | none]
      [ <hue> | none]
      [ / [<alpha-value> | none] ]? )
百分比 适用于 L 和 C
百分比参考范围 对于 L:0% = 0.0, 100% = 1.0
对于 C:0% = 0.0, 100% = 0.4
无效色相 ε C <= 0.000004
测试

OKLCh 中,第一个参数指定了 Oklch 亮度 L, 其解释与 oklab() 的亮度参数相同。

第二个参数是色度 C。 其最小有效值为 0, 最大值理论上没有上限 (但实际上不超过 0.5)。 如果提供的值为负值, 则在解析时将其钳制到 0

第三个参数是色相角 H。 它的解释类似于 <hue>hsl()lch() 参数, 但不会以相同的方式将色相映射到角度。0deg 指向正“a”轴(朝向紫红色), (360deg720deg 等也是如此);90deg 指向正“b”轴(朝向芥末黄),180deg 指向负“a”轴(朝向带绿色的青色), 270deg 指向负“b”轴(朝向天蓝色)。

有一个可选的第四个 <alpha-value> 分量, 用斜杠分隔, 表示 alpha 分量

如果 OKLCh 颜色的色度为 0% 或 0, 则色相分量是无效的。 如果 OKLCh 颜色的亮度为 0% 或 0, 或 100% 或 1.0, 由于色域映射到显示器,颜色将分别显示为黑色或白色。

 oklch(40.101% 0.12332 21.555);
 oklch(59.686% 0.15619 49.7694);
 oklch(0.65125 0.13138 104.097);
 oklch(0.66016 0.15546 134.231);
 oklch(72.322% 0.12403 247.996);
 oklch(42.1% 48.25% 328.4)

使用 oklaboklch 没有任何 Web 兼容性问题, 它们在本级规范中是新引入的,因此 oklab()oklch() 支持用逗号分隔所有参数的 旧版颜色语法。 在这些函数中使用逗号会产生错误。

9.5. 将 Lab 或 Oklab 颜色转换为 LCH 或 OKLCh 颜色

转换为极坐标形式非常简单:

  1. C = sqrt(a^2 + b^2)
  2. 如果 (C > epsilon) H = atan2(b, a) 否则 H 缺失
  3. L 相同

对于 a 和 b 的非常小的值(接近零的色度), 尽管视觉颜色没有从中性色轴发生变化, 但这些值的微小变化可能导致所报告的色相角剧烈摆动并基本上是随机的。 在 CSS 中,这意味着色相是 无效的, 在转换为 LCH 或 OKLCh 时被视为 缺失; 在非 CSS 环境中,这可能表现为缺失值,例如 NaN。

9.6. 将 LCH 或 OKLCh 颜色转换为 Lab 或 Oklab 颜色

转换为矩形坐标形式非常简单:

  1. 如果 H 缺失,a = b = 0
  2. 否则,
    1. a = C cos(H)
    2. b = C sin(H)
  3. L 保持不变

10. 预定义的色彩空间

CSS 提供了几种预定义的色彩空间, 包括 display-p3 [Display-P3], 这是一种典型的广色域空间,适用于当前的广色域显示器,prophoto-rgb,广泛用于摄影师, 以及 rec2020 [Rec.2020], 这是一种广播行业标准, 超广色域空间,几乎可以表示所有可见的真实世界色彩。

10.1. 指定预定义颜色:color() 函数

color() 函数允许在特定的、指定的 色彩空间 中指定颜色 (而不是大多数其他颜色函数操作的隐含 sRGB 色彩空间)。 它的语法如下:

color() = color( <colorspace-params> [ / [ <alpha-value> | none ] ]? )
<colorspace-params> = [ <predefined-rgb-params> | <xyz-params>]
<predefined-rgb-params> = <predefined-rgb> [ <number> | <percentage> | none ]{3}
<predefined-rgb> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020
<xyz-params> = <xyz-space> [ <number> | <percentage> | none ]{3}
<xyz-space> = xyz | xyz-d50 | xyz-d65
测试

color 函数接受指定颜色的参数,并在显式列出的色彩空间中指定。

它表示一个 无效颜色(如下所述), 或者一个 有效颜色

参数形式如下:

超出色域的颜色具有小于 0 或 0%,或者大于 1 或 100% 的分量值。 这些并不是无效的,且会保留用于中间计算; 但是在显示时,它们会使用相对比色意图进行 CSS 色域映射, 使得值(在显示色彩空间中)在实际值时落入 0/0% 到 1/100% 的范围内。

测试

使用 color() 没有 Web 兼容性问题, 因为它是在这个规范级别中新引入的,因此 color() 支持使用逗号分隔所有参数的 旧色彩语法。 在这个函数中使用逗号是错误的。

如果颜色是 无效颜色 或者 是 超出色域 的颜色, 那么该颜色 无法显示

如果指定的颜色 可以显示, (即它不是 无效颜色 并且不是 超出色域), 那么这就是 color() 函数的实际值。

如果指定的颜色 是一个 有效颜色无法显示, 那么实际值是从指定的颜色派生的,并经过 CSS 色域映射 用于显示。

如果颜色是 无效颜色, 那么使用的值是 不透明黑色

这个非常浓烈的酸橙色是 rec.2020 色域中的颜色:
color(rec2020 0.42053 0.979780 0.00579);

在 LCH 中,这种颜色是:

lch(86.6146% 160.0000 136.0088);

在 display-p3 中,这种颜色是:

color(display-p3 -0.6112 1.0079 -0.2192);

并且在 display-p3 中超出色域 (红色和蓝色为负值,绿色大于 1)。 如果您有一个 display-p3 屏幕,那么这种颜色是:

用于显示的颜色将是由色域映射自动生成的较不饱和的颜色。
这个示例有一个拼写错误! 一个强烈的绿色在 profoto-rgb 色域中被指定(实际上不存在这种色域)。 这使得它无效,因此使用的值是 不透明黑色
color(profoto-rgb 0.4835 0.9167 0.2188)

10.2. 预定义的 sRGB 颜色空间:sRGB 关键字

下面定义的 sRGB 预定义颜色空间 与用于传统 sRGB 颜色(如 rgb())的相同。

srgb
srgb [SRGB] 色彩空间接受三个数字参数,分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内。 白点是 D65

[SRGB] 指定了两种观看条件:编码典型[ICC] 建议使用编码条件进行颜色转换和最佳观看,这些值如下表所示。

sRGB 是 CSS 的默认色彩空间,用于所有旧版颜色函数。

它具有以下特性:

x y
红色色度 0.640 0.330
绿色色度 0.300 0.600
蓝色色度 0.150 0.060
白色色度 D65
传递函数 见下文
白色亮度 80.0 cd/m2
黑色亮度 0.20 cd/m2
图像状态 显示参考
百分比 允许用于 R、G 和 B
百分比参考范围 对于 R,G,B:0% = 0.0,100% = 1.0
let sign = c < 0? -1 : 1;
let abs = Math.abs(c);

if (abs <= 0.04045) {
  cl = c / 12.92;
}
else {
  cl = sign * (Math.pow((abs + 0.055) / 1.055, 2.4));
}

c 是经过伽马编码的红色、绿色或蓝色分量。 cl 是相应的线性光分量。

diagram of sRGB primaries and secondaries in LCH
sRGB 色彩空间在 LCH 中的可视化。显示了原色和二次色。
测试

10.3. 预定义的线性光 sRGB 颜色空间:srgb-linear 关键字

sRGB-linear 预定义颜色空间 与 srgb 相同,除了传递函数为线性光(无伽马编码)。

srgb-linear
srgb-linear [SRGB] 色彩空间接受三个数字参数, 分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内。 白点是 D65

它具有以下特性:

x y
红色色度 0.640 0.330
绿色色度 0.300 0.600
蓝色色度 0.150 0.060
白色色度 D65
传递函数 统一,见下文
白色亮度 80.0 cd/m2
黑色亮度 0.20 cd/m2
图像状态 显示参考
百分比 允许用于 R, G 和 B
百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0
cl = c;

c 是红、绿或蓝分量。 cl 是相应的线性光分量,且与 c 相同。

为避免带状伪影,需要更高的精度用于 srgb-linear 相较于 srgb

例如,这些是相同的颜色:
 color(srgb 0.691 0.139 0.259)
 color(srgb-linear 0.435 0.017 0.055)
测试

10.4. 预定义的 Display P3 颜色空间:display-p3 关键字

display-p3
display-p3 [Display-P3] 色彩空间接受三个数字参数, 分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内。 它使用与 [DCI-P3] 相同的主色度, 但使用 D65 白点,以及与 sRGB 相同的传递曲线。

现代显示器、电视、笔记本屏幕和手机屏幕能够显示全部或几乎全部的 display-p3 色域。

它具有以下特性:

x y
红色色度 0.680 0.320
绿色色度 0.265 0.690
蓝色色度 0.150 0.060
白色色度 D65
传递函数 与 srgb 相同
白色亮度 80.0 cd/m2
黑色亮度 0.80 cd/m2
图像状态 显示参考
百分比 允许用于 R, G 和 B
百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0
P3 原色和次原色在 LCH 中的图示
在 LCH 中可视化 P3 颜色空间。图中显示了原色和次原色(但在 sRGB 中,并不是正确的颜色)。为了对比,还显示了 sRGB 的原色和次原色,作为虚线圆圈。 P3 原色具有更高的色度。
测试

10.5. 预定义的 A98 RGB 颜色空间:a98-rgb 关键字

a98-rgb
a98-rgb 色彩空间接受三个数字参数, 分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内。 传递曲线是一个伽马函数,接近但不完全等于 1/2.2。

它具有以下特性:

x y
红色色度 0.6400 0.3300
绿色色度 0.2100 0.7100
蓝色色度 0.1500 0.0600
白色色度 D65
传递函数 256/563
白色亮度 160.0 cd/m2
黑色亮度 0.5557 cd/m2
图像状态 显示参考
百分比 允许用于 R, G 和 B
百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0
A98 原色和次原色在 LCH 中的图示
在 LCH 中可视化 A98 颜色空间。 图中显示了原色和次原色(但在 sRGB 中,并不是正确的颜色)。 为了对比,还显示了 sRGB 的原色和次原色,作为虚线圆圈。 a98 原色具有更高的色度,特别是在黄色、绿色和青色方面。
测试

10.6. 预定义的 ProPhoto RGB 颜色空间:prophoto-rgb 关键字

prophoto-rgb
prophoto-rgb 色彩空间接受三个数字参数, 分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内。 传递曲线是一个伽马值为 1/1.8 的伽马函数, 在黑色附近有一个小的线性部分。 白点是 D50,与 CIE Lab 使用的白点相同。因此, 转换为 CIE Lab 不需要色度适应步骤。

ProPhoto RGB 空间使用超饱和的、非物理可实现的原色。 这些原色的选择旨在提供广色域,特别是为了在色调操作下最小化色相偏移。 它通常用于数字摄影作为宽色域的颜色空间, 用于保存摄影图像的存档版本。 prophoto-rgb 颜色空间允许 CSS 指定与这些图像中 RGB 值相同的颜色。

ProPhoto RGB 空间最初由柯达开发,并在 [Wolfe] 中进行了描述。 它由 ISO 标准化为 [ROMM][ROMM-RGB]

白色亮度给出一个范围,视场耀斑(也就是黑色亮度)是此范围的 0.5% 到 1.0%。

它具有以下特性:

x y
红色色度 0.734699 0.265301
绿色色度 0.159597 0.840403
蓝色色度 0.036598 0.000105
白色色度 D50
传递函数 见下文
白色亮度 160.0 到 640.0 cd/m2
黑色亮度 见文本
图像状态 显示参考
百分比 允许用于 R, G 和 B
百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0
const E = 16/512;
let sign = c < 0? -1 : 1;
let abs = Math.abs(c);

if (abs <= E) {
  cl =  c / 16;
}
else {
  cl = sign * Math.pow(c, 1.8);
}

c 是伽玛编码的红、绿或蓝分量。 cl 是相应的线性光分量。

ProPhoto 原色和次原色在 LCH 中的图示
在 LCH 中可视化 ProPhoto RGB 颜色空间。 图中显示了原色和次原色(但在 sRGB 中,并不是正确的颜色)。 为了对比,还显示了 sRGB 的原色和次原色,作为虚线圆圈。 ProPhoto RGB 的原色和次原色具有更高的色度,但这种超宽色域的大部分 不对应于物理上可实现的颜色。
测试

10.7. 预定义的 ITU-R BT.2020-2 颜色空间:rec2020 关键字

rec2020
rec2020 [Rec.2020] 色彩空间接受三个数字参数, 分别表示颜色的红色、绿色和蓝色分量。 色域内的颜色,其三个分量均在 [0, 1] 范围内 (视频术语中的“全范围”)。 ITU Reference 2020 用于 超高清、4k 和 8k 电视。

这些原色是物理上可实现的,但实现起来很困难, 因为它们非常接近光谱轨迹。

当前显示器无法完全重现 rec2020 的全部色域。 随着显示技术的进步,覆盖率预计会逐渐提高。

它具有以下特性:

x y
红色色度 0.708 0.292
绿色色度 0.170 0.797
蓝色色度 0.131 0.046
白色色度 D65
传递函数 见下文,来自 [Rec.2020] 表 4
图像状态 显示参考
百分比 允许用于 R, G 和 B
百分比参考范围 对于 R,G,B: 0% = 0.0, 100% = 1.0
const α = 1.09929682680944 ;
const β = 0.018053968510807;

let sign = c < 0? -1 : 1;
let abs = Math.abs(c);

if (abs < β * 4.5 ) {
  cl = c / 4.5;
}
else {
  cl = sign * (Math.pow((abs + α -1 ) / α, 1/0.45));
}

c 是伽玛编码的红、绿或蓝分量。 cl 是相应的线性光分量。

rec2020 原色和次原色在 LCH 中的图示
在 LCH 中可视化 rec2020 颜色空间。 图中显示了原色和次原色(但在 sRGB 中,并不是正确的颜色)。 为了对比,还显示了 sRGB 的原色和次原色,作为虚线圆圈。 rec2020 的原色具有更高的色度。
测试

10.8. 预定义的 CIE XYZ 颜色空间:xyz-d50xyz-d65xyz 关键字

xyz-d50xyz-d65xyz
xyz 色彩空间接受三个数字参数, 分别表示 X、Y 和 Z 值。 它表示 CIE XYZ [COLORIMETRY] 色彩空间, 经过缩放以使漫射白的亮度 (Y) 为 1.0, 并且在必要时进行色度适应以匹配参考白。

xyz-d50 的参考白是 D50,而 xyz-d65xyz 的参考白是 D65

允许大于 1.0/100% 的值,并且不得被限制; Y 大于 1.0 的颜色表示比漫射白更亮的颜色。 小于 0/0% 的值不常见, 但可能由于色度适应而出现, 同样不得被限制。

它具有以下特性:

百分比 允许用于 X, Y, Z
百分比参考范围 对于 X, Y, Z: 0% = 0.0, 100% = 1.0
这些完全等价:
 #7654CD
 rgb(46.27% 32.94% 80.39%)
 lab(44.36% 36.05 -58.99)
 color(xyz-d50 0.2005 0.14089 0.4472)
 color(xyz-d65 0.21661 0.14602 0.59452)
These colors are exactly equivalent, and represent white:
 #FFFFFF
 color(xyz-d50 0.9643 1 0.8251)
 color(xyz-d65 0.9505 1 1.089)
测试

10.9. 将预定义的颜色空间转换为 Lab 或 Oklab

对于所有预定义的 RGB 颜色空间,转换为 Lab 需要几个步骤,虽然在实践中,除第一个步骤外,其余步骤都是线性计算,并且可以组合在一起。

  1. 从伽马编码的 RGB 转换为线性光 RGB(撤销伽马编码)
  2. 从线性 RGB 转换为 CIE XYZ
  3. 如果需要,使用线性 Bradford 变换将 D65 白点(由 sRGBdisplay-p3a98-rgbrec2020 使用) 转换为 Lab 中使用的 D50 白点。 prophoto-rgb 已经具有 D50 白点。
  4. 将经过 D50 适应的 XYZ 转换为 Lab

转换为 Oklab 类似,但色度适应步骤仅对 prophoto-rgb 需要。

  1. 从伽马编码的 RGB 转换为线性光 RGB(撤销伽马编码)
  2. 从线性 RGB 转换为 CIE XYZ
  3. 如果需要,使用线性 Bradford 变换将 D50 白点(由 prophoto-rgb 使用) 转换为 Oklab 中使用的 D65 白点。
  4. 将经过 D65 适应的 XYZ 转换为 Oklab

这些转换的 JavaScript 示例代码可以在 § 18 颜色转换的示例代码 中找到。

10.10. 将 Lab 或 Oklab 转换为预定义的 RGB 颜色空间

从 Lab 转换为诸如 display-p3rec2020 等预定义空间也需要多个步骤,并且在实践中,除最后一步外,其余步骤都是线性计算,并且可以组合在一起。

  1. 将 Lab 转换为(D50 适应的)XYZ
  2. 如果需要,使用线性 Bradford 变换将 Lab 使用的 D50 白点转换为 sRGB 和大多数其他 RGB 空间中使用的 D65 白点。 prophoto-rgb 不需要此步骤。
  3. 将(D65 适应的)CIE XYZ 转换为线性 RGB
  4. 将线性光 RGB 转换为 RGB(执行伽马编码)

从 Oklab 转换类似,但色度适应步骤仅对 prophoto-rgb 需要。

  1. 将 Oklab 转换为(D65 适应的)XYZ
  2. 如果需要,使用线性 Bradford 变换将 Oklab 使用的 D65 白点转换为 prophoto-rgb 中使用的 D50 白点。
  3. 将(D65 适应的)CIE XYZ 转换为线性 RGB
  4. 将线性光 RGB 转换为 RGB(执行伽马编码)

这些转换的 JavaScript 示例代码可以在 § 18 颜色转换的示例代码 中找到。

实现可能选择以其他方式实现这些步骤(例如,使用具有相对色度渲染意图的 ICC 配置文件),只要源和目标色域中的颜色的结果相同即可。

10.11. 在预定义 RGB 颜色空间之间转换

从一个预定义的 RGB 颜色空间转换到另一个需要多个步骤,其中一个步骤仅在白点不同的情况下需要。要从 src 转换到 dest

  1. 从伽马编码的 srcRGB 转换为线性光 srcRGB(撤销伽马编码)
  2. 将线性 srcRGB 转换为 CIE XYZ
  3. 如果 srcdest 具有不同的白点,则将 XYZ 值从 srcWhite 转换为 destWhite,使用线性 Bradford 转换。
  4. 将 CIE XYZ 转换为线性 destRGB
  5. 将线性光 destRGB 转换为 destRGB(进行伽马编码)

这些预定义 RGB 颜色空间的转换的 JavaScript 示例代码可以在 § 18 颜色转换的示例代码 中找到。

10.12. 简单的 Alpha 合成

绘制时,实施必须根据 《合成与混合》第 5.1 节“简单 alpha 合成”中的规则处理 alpha 值 [Compositing]

11. 转换颜色

测试

本节提供的算法在后面使用,不需要测试。


颜色可以从一个颜色空间转换到另一个颜色空间,前提是没有色域映射,并且每个颜色空间都可以表示超出色域的颜色(对于 RGB 空间,这意味着传递函数在扩展范围内定义),那么(受限于数值精度和舍入误差)两个颜色看起来会相同,表示相同的颜色感受。

要将源颜色空间 src 中的颜色 col1,白点为 src-white,转换为目标颜色空间 dest 中的颜色 col2,白点为 dest-white

  1. 如果 src圆柱极坐标颜色表示,首先将 col1 转换为对应的 矩形直角坐标颜色表示,并将其作为新的 col1。用零替换任何缺失的组件
  2. 如果 src 不是线性光表示,则将其转换为线性光(撤销伽马编码),并将其作为新的 col1
  3. col1 转换为白点为 src-white 的 CIE XYZ,并将其记为 xyz
  4. 如果 dest-white 不同于 src-white,则使用线性 Bradford 色度适应变换使 xyz 适应 dest-white,并将其作为新的 xyz
  5. 如果 dest圆柱极坐标颜色表示,则 dest-rect 为对应的 矩形直角坐标颜色表示。否则,令 dest-rectdest
  6. xyz 转换为 dest,然后应用任何传递函数(伽马编码),得到 col2
  7. 如果 dest 是物理输出颜色空间,如显示器,那么 col2 必须 CSS 色域映射,使其可以显示
  8. 如果 dest-rect 不等于 dest,换句话说,dest圆柱极坐标颜色表示,则从 dest-rect 转换为 dest,并将其作为 col2。这可能会产生缺失的组件

12. 颜色插值

颜色插值发生在渐变、合成、滤镜、过渡、动画以及颜色混合和颜色修改函数中。

两个 <color> 值之间的插值通过执行以下步骤进行:

  1. 检查两种颜色的类似分量,这些分量将被沿用
  2. 将它们转换为给定的色彩空间, 下文将其称为插值色彩空间。 如果一种或两种颜色已处于插值色彩空间中, 此转换会将任何无效分量更改为缺失
  3. (如果需要)在转换后的颜色中重新插入沿用的
  4. (如果需要)根据所选的 <hue-interpolation-method> 修正色相
  5. 将颜色分量更改为预乘形式
  6. 分别对颜色的计算值的每个分量进行线性插值
  7. 撤销预乘

可以插值到或从 currentcolor。为此使用的数值为使用的值。

12.1. 插值的颜色空间

CSS 中的各种特性依赖于插值颜色。

示例包括:

混合或组合颜色时,结果会因所使用的 插值颜色空间 不同而有所差异。因此,不同的插值用途可能适用于不同的颜色空间。

这些功能统称为宿主语法

为了允许宿主语法指示插值色彩空间, 本规范导出一个 color-interpolation-method 产生式。 它本身不被本规范使用, 仅暴露出来以便其他规范可以使用它;例如,请参阅 CSS 图像 4 § 3.1 线性渐变:linear-gradient() 表示法中的用法。

宿主语法应定义每种情况下默认的插值色彩空间应该是什么, 并且最好提供语法供作者覆盖此默认值。 如果此类语法是属性值的一部分,则应使用下面定义的 color-interpolation-method 产生式, 以便其他规范轻松参考。 这确保了 CSS 之间的一致性, 并且有关如何执行颜色插值的进一步自定义可以自动渗透到所有 CSS 中。

<color-space> = <rectangular-color-space> | <polar-color-space>
<rectangular-color-space> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020 | lab | oklab | xyz | xyz-d50 | xyz-d65
<polar-color-space> = hsl | hwb | lch | oklch
<hue-interpolation-method> = [ shorter | longer | increasing | decreasing ] hue
<color-interpolation-method> = in [ <rectangular-color-space> | <polar-color-space> <hue-interpolation-method>? ]

<rectangular-color-space><polar-color-space> 的定义中的关键字各自引用其对应的颜色空间,这些颜色空间在 CSS 中既可以通过同名的函数语法表示,也可以(如果没有这样的函数)通过 <ident>color() 函数中表示。

测试

如果宿主语法没有定义颜色插值应在哪个颜色空间中进行,则默认使用 Oklab。

在特定实例中偏好在 sRGB 中进行插值的作者可以通过显式指定 sRGB 作为插值色彩空间来选择旧的行为, 例如在期望该结果的特定渐变上。

如果待插值的颜色超出了插值色彩空间的色域, 那么一旦转换到该空间, 它们将包含超出范围的值。

这些值不会被裁剪,而是按原样进行插值。

12.2. 插值时的缺失组件

在将两个颜色转换为 插值颜色空间 的过程中,任何 缺失组件 都会被替换为值 0。

因此,插值两个颜色的第一步是对输入颜色中的任何 缺失组件 进行分类,并将其与 插值颜色空间 的组件进行比较。如果找到任何作为 缺失组件类似组件,它们将被带入并在预乘和线性插值之前重新插入转换后的颜色中。

类似组件 如下:

类别 组件
红色 r,x
绿色 g,y
蓝色 b,z
亮度 L
色彩度 C, S
色相 H
对立 a a
对立 b b
Alpha alpha

注意:对于此分类的目的,XYZ 空间被视为超饱和的 RGB 空间。此外,尽管饱和度依赖于亮度,但在这里它仍与色度归为同一类别。HWB 的白度和黑度在其他颜色空间中没有对应的类似组件。

例如,如果要在 OKLCh 中插值这两个颜色,CIE LCH 颜色中的缺失色相是与 OKLCh 色相类似的,因此将被带入,而第二个颜色中的缺失蓝色组件与任何 OKLCh 组件不相似,因此不会被带入:
 lch(50% 0.02 none)
 color(display-p3 0.7 0.5 none)

转换为:

 oklch(56.897% 0.0001 0)
 oklch(63.612% 0.1522 78.748)

重新插入带入的 缺失组件 后,要插值的两个颜色为:

 oklch(56.897% 0.0001 none)
 oklch(63.612% 0.1522 78.748)

如果具有带入的 缺失组件 的颜色与另一个没有该组件缺失的颜色进行插值,则 缺失组件 的值被视为另一个颜色的组件值。

因此,带入步骤必须在处理任何 无效组件 之前执行。

例如,如果插值以下两个颜色,第二个颜色缺失色相:
 oklch(78.3% 0.108 326.5)
 oklch(39.2% 0.4 none)

实际要插值的颜色为:

 oklch(78.3% 0.108 326.5)
 oklch(39.2% 0.4 326.5)

而不是:

 oklch(78.3% 0.108 326.5)
 oklch(39.2% 0.4 0)

如果沿用的缺失分量是 alpha,则颜色必须使用此沿用的值进行预乘, 而不是使用颜色转换产生的零值。

例如,如果对这两种颜色进行插值,其中第二种颜色缺少 alpha:
 oklch(0.783 0.108 326.5 / 0.5)
 oklch(0.392 0.4 0 / none)

那么实际要插值的颜色是

 oklch(78.3% 0.108 326.5 / 0.5)
 oklch(39.2% 0.4 0 / 0.5)

得到预乘的 OKLCh 值为 [0.3915, 0.054, 326] 和 [0.196, 0.2, 0]。

如果两种颜色都缺少某个给定的分量, 则插值后的颜色 也将缺少该分量。

12.3. 带有 Alpha 的插值

当要插值的颜色不完全不透明时, 它们首先按如下方式进行预乘

要从预乘颜色值中获取颜色值,

测试
为什么预乘 Alpha 有用?

使用预乘表示法插值颜色往往比非预乘表示法产生更具吸引力的过渡,尤其是在从完全不透明颜色过渡到完全透明时。

请注意,如果透明度或颜色保持不变(例如在 rgba(255, 0, 0, 100%)(不透明的红色)和 rgba(0,0,255,100%)(不透明的蓝色),或 rgba(255,0,0,100%)(不透明的红色)和 rgba(255,0,0,0%)(透明的红色)之间进行过渡),则不论在预乘还是非预乘颜色空间中进行颜色插值,结果都是相同的。只有当两个端点之间的颜色和透明度都不同的时候,才会出现差异。

以下示例说明了通过预乘值(在这种情况下是 sRGB,因为所有涉及的颜色都是传统颜色)进行过渡的渐变与通过非预乘值(不正确地)进行过渡的渐变之间的区别。在这两个示例中,渐变是在白色背景上绘制的。两种渐变都可以用以下值表示:
linear-gradient(90deg, red, transparent, blue)

使用预乘颜色时,"透明"的过渡总是看起来很不错:

(图像需要 SVG)

另一方面,如果在非预乘空间中错误地进行渐变,渐变的中心部分将明显呈现灰色,因为"透明"实际上是 rgba(0,0,0,0)(透明的黑色)的简写,这意味着红色在失去不透明度时过渡到黑色,蓝色的过渡也是如此:

(图像需要 SVG)
例如,在 sRGB 色彩空间中对两个 sRGB 颜色 rgb(24% 12% 98% / 0.4) 和 rgb(62% 26% 64% / 0.6) 进行插值时,它们首先被转换为预乘形式 [9.6% 4.8% 39.2%] 和 [37.2% 15.6% 38.4%],然后再进行插值。

线性插值这些颜色的中点将是 [23.4% 10.2% 38.8%],使用 alpha 值 0.5,撤销预乘后的颜色为 rgb(46.8% 20.4% 77.6% / 0.5)。

在 Lab 色彩空间中插值两个颜色 rgb(76% 62% 03% / 0.4) 和 color(display-p3 0.84 0.19 0.72 / 0.6),首先将它们转换为 Lab 表示形式 lab(66.927% 4.873 68.622 / 0.4) lab(53.503% 82.672 -33.901 / 0.6),然后将 L、a 和 b 坐标进行预乘后再插值 [26.771% 1.949 27.449] 和 [32.102% 49.603 -20.341]。

线性插值的中点将是 [29.4365% 25.776 3.554],使用 alpha 值 0.5,撤销预乘后的颜色为 lab(58.873% 51.552 7.108 / 0.5)。

在保持色度的 LCH 色彩空间中插值相同的两个颜色 rgb(76% 62% 03% / 0.4) 和 color(display-p3 0.84 0.19 0.72 / 0.6),首先将它们转换为 LCH 表示形式 lch(66.93% 68.79 85.94 / 0.4) lch(53.5% 89.35 337.7 / 0.6),然后将 L 和 C 坐标(但不是 H)进行预乘后再插值 [26.771% 27.516 85.94] 和 [32.102% 53.61 337.7]。

沿着 较短 色调弧(默认)进行线性插值的中点为 [29.4365% 40.563 31.82],使用 alpha 值 0.5,撤销预乘后的颜色为 lch(58.873% 81.126 31.82 / 0.5)。

有关 alpha 预乘和撤销预乘的示例 JavaScript 代码(适用于极坐标和直角坐标颜色空间),请参见 § 18 颜色转换示例代码

12.4. 色调插值

对于具有色调角度的颜色函数(如 LCH、HSL、HWB 等),有多种插值方式。 由于大于 360° 的弧度通常不理想, 色调角度在插值之前会被调整 使得每个分量的插值小于 360°,通常小于 180°。

主语法可以指定以下任何一种算法用于色调插值 (以下角度单位为度,但无论如何指定,逻辑都是相同的)。 指定色调插值策略已是 <color-interpolation-method> 语法的一部分 通过 <hue-interpolation-method> 标记来完成。

除非另有说明,如果主语法未选择特定的色调插值算法,默认使用 较短 弧度。

测试

注意: 请记住, 如果要插值的颜色尚未处于指定的插值颜色空间中, 那么将其转换会将任何无效分量变为缺失分量

12.4.1. 较短

色调角度通过插值取起始和结束色调之间的较短弧。

例如,在 OKLCh 中从红色 oklch(0.6 0.24 30) 到黄色 oklch(0.8 0.15 90) 之间插值的中点色调角度为 30 + (90 - 30) * 0.5 = 60 度, 沿着两个颜色之间的较短弧, 得到深橙色 oklch(0.7 0.195 60)

角度调整使得 θ₂ - θ₁ ∈ [-180, 180]。伪 JavaScript 代码:

if (θ₂ - θ₁ > 180) {
  θ₁ += 360;
}
else if (θ₂ - θ₁ < -180) {
  θ₂ += 360;
}

12.4.2. 较长

色调角度通过插值取起始和结束色调之间的较长弧。

例如,在 OKLCh 中从红色 oklch(0.6 0.24 30) 到黄色 oklch(0.8 0.15 90) 之间插值的中点色调角度为 (30 + 360 + 90) * 0.5 = 240 度, 沿着两个颜色之间的较长弧, 得到天蓝色 oklch(0.7 0.195 240)

角度调整使得 θ₂ - θ₁ ∈ {(-360, -180], [180, 360)}。伪 JavaScript 代码:

if (0 < θ₂ - θ₁ < 180) {
  θ₁ += 360;
}
else if (-180 < θ₂ - θ₁ <= 0) {
  θ₂ += 360;
}

12.4.3. 递增

色调角度被插值为使其在从第一个颜色到第二个颜色的过程中, 始终保持递增。 如果角度增加到 360,则重置为零, 然后继续增加。

根据两个角度之间的差异, 这将看起来与较短较长类似。但是,如果其中一个色调角度被动画化, 并且色调角度差异经过 180 度, 则插值不会翻转到另一弧。

例如,在 OKLCh 中从深棕色 oklch(0.5 0.1 30) 到 青绿色 oklch(0.7 0.1 190) 之间插值的中点色调角度为 (30 + 190) * 0.5 = 110 度, 得到卡其色 oklch(0.6 0.1 110)。

但是,如果第二种颜色的色调被动画化为 oklch(0.7 0.1 230), 则插值的中点为 (30 + 230) * 0.5 = 130 度, 继续沿相同的递增方向, 得到另一种绿色 oklch(0.6 0.1 130) 而不是在动画的过程中翻转到对方颜色。

角度调整使得 θ₂ - θ₁ ∈ [0, 360)。伪 JavaScript 代码:

if (θ₂ < θ₁) {
  θ₂ += 360;
}

12.4.4. 递减

色调角度被插值为使其在从第一个颜色到第二个颜色的过程中, 始终保持递减。 如果角度减小到 0,则重置为 360, 然后继续递减。

根据两个角度之间的差异, 这将看起来与较短较长类似。但是,如果其中一个色调角度被动画化, 并且色调角度差异经过 180 度, 则插值不会翻转到另一弧。

例如,在 OKLCh 中从深棕色 oklch(0.5 0.1 30) 到 青绿色 oklch(0.7 0.1 190) 之间插值的中点色调角度为 (30 + 360 + 190) * 0.5 = 290 度, 得到紫色 oklch(0.6 0.1 290)。

但是,如果第二种颜色的色调被动画化为 oklch(0.7 0.1 230), 则插值的中点为 (30 + 360 + 230) * 0.5 = 310 度, 继续沿相同的递减方向, 得到另一种紫色 oklch(0.6 0.1 310) 而不是在动画的过程中翻转到对方颜色。

角度调整使得 θ₂ - θ₁ ∈ (-360, 0]。伪 JavaScript 代码:

if (θ₁ < θ₂) {
  θ₁ += 360;
}

13. 色域映射

13.1. 色域映射简介

注意: 本节为文档其他地方描述的具体要求提供了重要的上下文。

本节非规范性内容

测试

本节不是规范性的,不需要测试。


当一个原始颜色空间中的颜色 被转换到另一个具有较小色域的目标颜色空间时, 一些颜色将超出目标色域。

对于中间颜色计算, 这些超出色域的值会被保留。 但是,如果目标是显示设备 (如屏幕或打印机), 那么超出色域的值必须转换为 一个在色域内的颜色。

色域映射是找到一个 在视觉上变化最小的在色域内的颜色的过程。

13.1.1. 裁剪

最简单且最不理想的方法 就是简单地将分量值 裁剪到显示范围内。 这会改变 三原色(对于 RGB 显示器)的比例, 导致色调偏移。

例如, 考虑颜色 color(srgb-linear 0.5 1 3)。 因为这是一个线性光颜色空间, 我们可以比较三个分量的强度, 看到蓝色光的量是绿色的三倍, 而红色光的量是绿色的一半。 蓝色主色光的量是红色的六倍。 在 OKLCh 中,这种颜色的色调角为 265.1°

如果现在将该颜色裁剪, 使其进入 sRGB 色域, 我们得到 color(srgb-linear 0.5 1 1)。 蓝色光的量与绿色相同。 在 OKLCh 中,这种颜色的色调角为 196.1°, 相比原始颜色变化了 69°。

13.1.2. 最接近的颜色 (MINDE)

更好的方法是在一个感知均匀的颜色空间中通过找到最接近的在色域内的颜色进行映射 (所谓的最小 ΔE 或 MINDE)。 显然,这种技术的成功 取决于 色域映射颜色空间的均匀程度 以及所使用的 deltaE 函数的预测精度。

但是,在进行色域映射时, 色调的变化特别令人反感; 色度的变化更容易接受, 而 如果另一种选择是较大幅度的色度降低, 那么轻微的亮度变化也是可以接受的。 MINDE 对每个维度的变化赋予相同的权重, 因此产生次优的结果。

13.1.3. 色度减少

为了改进 MINDE 算法, 颜色在一个感知均匀的极坐标颜色空间中映射, 通过保持色调不变, 并减少色度直到颜色落入色域内。

在此示例中,Display P3 原黄色 (color(display-p3 1 1 0)) 正在映射到 sRGB 显示器。 色域映射的颜色空间为 OKLCh。
color(display-p3 1 1 0)

color(srgb 1 1 -0.3463)

即为

color(oklch 0.96476 0.24503 110.23)

通过逐步减少色度分量 直到得到的颜色落入 sRGB 色域内 (即没有分量为负值或大于 1), 得到色域映射后的颜色。

   color(oklch 0.96476 0.21094 110.23)

即为

   color(srgb 0.99116 0.99733 0.00001)
OKLCh 颜色空间的恒色调切片。 垂直轴表示亮度, 水平轴表示色度。 要映射的颜色, 显示为黄色圆圈, 在保持色调和亮度不变的情况下, 逐步减少色度。 因此颜色沿图中栗色线移动, 向左侧的中性色轴靠近。 sRGB 色域的边界 用绿色表示。

实际的实现将比线性减少收敛更快; 可以通过二分查找, 或计算恒色调和亮度线与色域边界的几何交点来实现。

13.1.4. 过度的色度减少

此外,对于某些颜色,特别是非常浅的颜色(如黄色和青色),这种简单的方法会产生次优的结果, 如果色域边界的上边缘较浅,甚至略微凹陷。 恒亮度线可能刚好位于色域边界之上, 导致在这些情况下色度过低。

颜色空间的选择会影响色域映射颜色的可接受性。

在此示例中,Display P3 原黄色 (color(display-p3 1 1 0) 在 CIE LCH 颜色空间中逐步减少色度。
在该图的上半部分, sRGB 色域内的颜色按原样显示。 Display P3 色域内(但在 sRGB 之外)的颜色用鲑鱼色表示。 Display P3 色域之外的颜色用红色表示。 图的下半部分显示了 Display P3 红、绿和蓝分量的线性光强度。
可以看到,CIE LCH 色度的减少使红色强度曲线向上,超出了 Display P3 的色域; 当它再次下降时,色度已经非常低。 在 CIE LCH 中进行简单的色域映射会产生不令人满意的结果。
在此示例中,Display P3 原色黄 (color(display-p3 1 1 0) 的色度逐渐降低,但这次是在 OKLCh 色彩空间中。
在此图的上半部分, sRGB 色域内的颜色按原样显示。 Display P3 色域内(但在 sRGB 之外)的颜色显示为鲑鱼色。 Display P3 色域之外的颜色显示为红色。 图的下半部分显示了 Display P3 红色、绿色和蓝色分量的线性光强度。
可以看出,OKLCh 色度的降低表现更好。 颜色不会超出 Display P3 色域,并且由此产生的色域映射黄色具有良好的色度。 在 OKLCH 中进行简单的色域映射会得到可接受的结果。

13.1.5. 带局部剪裁的色度减少

可以改进简单的色度减少算法: 在每一步中, 计算当前映射颜色和该颜色的剪裁版本之间的颜色差异。 如果当前颜色超出色域边界, 但它与剪裁版本之间的颜色差异 低于刚好可见差异(JND)的阈值, 则返回该颜色的剪裁版本作为映射结果。 实际上,这在每个阶段都在进行 MINDE 映射, 但受限于色调和亮度变化非常小, 因此不明显。

在此示例中,Display P3 原黄色 (color(display-p3 1 1 0) 在 CIE LCH 颜色空间中逐步减少色度, 并使用了局部剪裁修改。
在该图的上半部分, sRGB 色域内的颜色按原样显示。 Display P3 色域内(但在 sRGB 之外)的颜色用鲑鱼色表示。 Display P3 色域之外的颜色用红色表示。 图的下半部分显示了 Display P3 红、绿和蓝分量的线性光强度。
可以看到,CIE LCH 色度的减少仍然使红色强度曲线上升,超出了 Display P3 的色域; 但比之前减少了,并且更快地找到了 sRGB 边界。 在 CIE LCH 中使用局部剪裁进行色域映射会产生可接受的结果。
在此示例中,Display P3 原色黄 (color(display-p3 1 1 0) 的色度逐渐降低,但这次是在 OKLCh 色彩空间中 并使用局部裁剪修改。
在此图的上半部分, sRGB 色域内的颜色按原样显示。 Display P3 色域内(但在 sRGB 之外)的颜色显示为鲑鱼色。 Display P3 色域之外的颜色显示为红色。 图的下半部分显示了 Display P3 红色、绿色和蓝色分量的线性光强度。
可以看出,OKLCh 色度的降低本已表现良好, 通过局部裁剪修改进一步改善。 在 CIE LCH 中使用局部裁剪进行简单色域映射会产生极佳的结果。

13.1.6. 偏离感知均匀性:色相曲率

使用 CIE LCH 颜色空间 和 deltaE2000 距离度量, 对于色相范围在 270° 至 330° 的颜色, 结果已知会产生显著的色相偏移, 从而导致次优结果。

CIE LCH 颜色空间的恒色相切片, 色相角为 301.37° 对应于 sRGB 的原蓝色。 垂直轴是亮度,水平轴是色度。 在 25 到 75 的色度范围内,色相明显偏紫, 在 100 到 131 之间逐渐变为蓝色。 同样的现象在 131 之后继续, 但不能在 sRGB 显示器上显示出来。

使用 OKLCh 颜色空间 和 deltaEOK 距离度量 在所有色相角下避免了此问题。

OKLCh 颜色空间的恒色相切片, 色相角为 264.06° 对应于 sRGB 的原蓝色。 垂直轴是亮度,水平轴是色度。 色相在所有色度值下明显保持不变, 直到 0.315(此色相下的 sRGB 限制)。 尽管这一点无法在 sRGB 图上显示, 但它在此之后继续保持恒定。

13.2. CSS 色域映射到 RGB 目标

测试

颜色的实际值并未暴露给脚本,这使得以自动化方式进行测试变得困难。


CSS 色域映射算法 应用于 超出 RGB 显示器色域的单个标准动态范围 (SDR) CSS 颜色, 因此需要进行 CSS 色域映射

它实现了一种相对比色意图, 色域内的颜色不变。

注意: 其他情况, 特别是映射到打印机色域, 当最大黑位显著高于零时, 需要使用不同的算法, 这些算法将对齐相应的黑点和白点, 从而在降低色度的同时, 使非常亮和非常暗的颜色发生亮度变化。

注意: 该算法适用于单个独立的颜色; 对于图像, 邻近像素之间的关系很重要, 目标是保留细节和纹理, 因此使用感知呈现意图更为合适, 在这种情况下, 色域内的颜色可能会发生变化。

CSS 色域映射发生在 OKLCh 颜色空间 中, 使用的颜色差异公式为 deltaEOK。 使用了 local-MINDE 改进。

对于在亮度轴上超出范围的颜色, 如果亮度大于或等于 1.0, 则返回目标颜色空间中的白色, 如果亮度小于或等于 0.0, 则返回目标颜色空间中的黑色。

对于二分搜索实现, 在搜索的每一步中, 计算当前映射颜色和该颜色的剪裁版本之间的 deltaEOK。 如果当前颜色 色域边界之外, 但它与剪裁版本之间的 deltaEOK 低于 刚好可见差异(JND) 的阈值, 则返回该颜色的剪裁版本作为映射结果。

对于几何实现, 在找到确切的交点后, 沿恒亮度线向外投射(朝向更高色度),直到以下任一情况发生:

然后返回该颜色的剪裁版本作为映射结果。

对于 OKLCh 颜色空间, 一个 JND 是 OKLCh 差异的 0.02。

注意: 在 CIE Lab 颜色空间中, 亮度组件的范围是 0 到 100, 使用 deltaE2000, 一个 JND 是 2。 因为 Oklab 和 OKLCh 中亮度的范围是 0 到 1, 使用 deltaEOK, 一个 JND 要小 100 倍。

13.2.1. 二分搜索色域映射算法与局部 MINDE 的示例伪代码

将颜色空间 origin color space 中的颜色 origin 映射到目标颜色空间 destination 的色域中,称为
  1. 如果 destination 没有色域限制(XYZ-D65、XYZ-D50、Lab、LCH、Oklab、OKLCh),则将 origin 转换为 destination 并将其作为色域映射后的颜色返回
  2. originorigin color space 转换为 OKLCh 色彩空间,记为 origin_OKLCh
  3. 如果 origin_OKLCh 的亮度大于或等于 100%,则将 `oklab(1 0 0 / origin.alpha)` 转换为 destination 并将其作为色域映射后的颜色返回
  4. 如果 origin_OKLCh 的亮度小于或等于 0%,则将 `oklab(0 0 0 / origin.alpha)` 转换为 destination 并将其作为色域映射后的颜色返回
  5. 定义 inGamut(color) 函数,当传入颜色在 destination 色域内时返回 true。对于 HSL 和 HWB,当颜色在 sRGB 色域内时返回 true。
  6. 如果 inGamut(origin_OKLCh) 为 true,则将 origin_OKLCh 转换为 destination 并将其作为色域映射后的颜色返回
  7. 否则,定义 delta(one, two) 函数,返回颜色 one 与颜色 two 的 deltaEOK 值
  8. JND 设为 0.02
  9. epsilon 设为 0.0001
  10. 定义 clip(color) 函数,将 color 转换为 destination,将每个分量限制在该分量的参考范围内,并返回结果
  11. current 设为 origin_OKLCh
  12. clipped 设为 clip(current)
  13. E 设为 delta(clipped, current)
  14. 如果 E < JND
    1. 返回 clipped 作为色域映射后的颜色
  15. min 设为零
  16. max 设为 origin_OKLCh 的 OKLCh 色度
  17. 定义 min_inGamut 布尔值,表示 min 是否仍在色域内,并将其设为 true
  18. 当 (max - min 大于 epsilon) 时,重复以下步骤
    1. chroma 设为 (min + max) /2
    2. current 的色度分量设为 chroma
    3. 如果 min_inGamut 为 true 且 inGamut(current) 也为 true,则将 min 设为 chroma 并继续重复这些步骤
    4. 否则,执行以下步骤:
      1. clipped 设为 clip(current)
      2. E 设为 delta(clipped, current)
      3. 如果 E < JND
        1. 如果 (JND - E < epsilon),返回 clipped 作为色域映射后的颜色
        2. 否则,
          1. min_inGamut 设为 false
          2. min 设为 chroma
      4. 否则,将 max 设为 chroma 并继续重复这些步骤
  19. 返回 clipped 作为色域映射后的颜色

14. 解析 <color>

除非为特定属性另有规定,指定的颜色将解析为 计算颜色,然后进一步解析为 使用颜色,如下所述。

解析值<color> 是它的 使用值

测试

14.1. 解析 sRGB 值

这适用于:

适用于:

如果 sRGB 颜色由作者显式指定为命名颜色, 或作为系统颜色, 则声明值是该命名或系统颜色,转换为ASCII 小写。 计算值和使用值 是相应的 sRGB 颜色, 与指定的 alpha 分量配对 (在限制到 [0, 1] 范围后, 如果未指定则默认为不透明)。

以下作者提供的混合大小写形式具有全小写的声明值。

 pUrPlE
 purple

否则,声明值、计算值和使用值 是相应的 sRGB 颜色, 与指定的 alpha 分量配对 (在限制到 [0, 1] 范围后, 如果未指定则默认为不透明)。

由于历史原因,当 sRGB 颜色中的 calc() 解析为单个值时, 声明值序列化时不带 "calc(" ")" 包装器。

例如,如果颜色给定为 rgb(calc(64 * 2) 127 255), 声明值将是 rgb(128 127 255) 而不是 rgb(calc(128) 127 255)。
例如,如果颜色给定为 hsl(38.82 calc(2 * 50%) 50%), 声明值将是 rgb(255 165.2 0), 因为在 HSL 到 RGB 的转换过程中,calc() 会丢失。

同样由于历史原因, 当 calc() 简化为单个值时, 颜色值被限制在 [0.0, 255.0] 范围内。

例如,如果颜色给定为 rgb(calc(100 * 4) 127 calc(20 - 35)), 声明值将是 rgb(255 127 0) 而不是 rgb(calc(400) 127 calc(-15))。

这种限制也处理了诸如 Infinity-InfinityNaN 之类的值,它们将分别限制为 255、0 和 0。

例如,以下值的计算值

 hsl(38.824 100% 50%)

 rgb(255, 165, 0)
测试

14.2. 解析 Lab 和 LCH 值

这适用于 lab()lch() 值。

声明值、计算值和使用值 是相应的 CIE Lab 或 LCH 颜色 (在限制 L、C 和 H 后) 与指定的 alpha 分量配对 (作为 <number>,而非 <percentage>; 如果未指定则默认为不透明)。

例如,以下值的计算值

 lch(52.2345% 72.2 56.2 / 1)

 lch(52.2345% 72.2 56.2)
测试

14.3. 解析 Oklab 和 OKLCh 值

这适用于 oklab()oklch() 值。

声明值、计算值和使用值 是相应的 Oklab 或 OKLCh 颜色 (在限制 L、C 和 H 后) 与指定的 alpha 分量配对 (作为 <number>,而非 <percentage>; 如果未指定则默认为不透明)。

例如,

 oklch(42.1% 0.192 328.6 / 1)

的计算值是

 oklch(42.1% 0.192 328.6)
Tests

14.4. 解析 color() 函数的值

声明值、计算值和使用值 是指定色彩空间中的颜色, 与指定的 alpha 分量配对 (作为 <number>,而非 <percentage>; 如果未指定则默认为不透明)。

例如,

 color(display-p3 0.823 0.6554 0.2537 /1)

的计算值是

 color(display-p3 0.823 0.6554 0.2537)

对于在 xyz 颜色空间 中指定的颜色, 这是 xyz-d65 颜色空间 的别名, 计算值和使用值 采用 xyz-d65 颜色空间

例如,

 color(xyz 0.472 0.372 0.131)

的计算值是

 color(xyz-d65 0.472 0.372 0.131)
Tests

14.5. 解析其他颜色

这适用于系统颜色(包括 <deprecated-color>),transparent, 和 currentcolor

每个 <system-color> 关键字 和 <deprecated-color> 关键字 的声明值是其自身。 计算值 是其色彩空间中的相应颜色。 然而,这类颜色不得被 "强制颜色模式"改变。

例如,在这个 html 中:
<button style="color:  ButtonText; background:  ButtonFace"></button>

color 属性的声明值是 "ButtonText", 而计算值可能是,例如, rgb(0, 0, 0)。

transparent 的声明值是 "transparent", 而计算值和使用值是透明黑色

currentcolor 关键字计算为其自身。

color 属性中, currentcolor 的使用值是 已解析的继承值。 在任何其他属性中, 其使用值是同一元素上 color 属性的使用值。

注意:这意味着如果 currentcolor 值被继承, 它是作为关键字被继承的, 而不是作为 color 属性的值, 所以后代元素将使用它们自己的 color 属性 来解析它。

例如,给定以下 HTML:
<div>
  <p>假设这个示例文本足够长,可以在多行中换行。
  </p>
</div>

和以下 CSS:

div {
  color:  forestgreen;
  text-shadow: currentColor;
}
p {
  color:  mediumseagreen;
} 
p::firstline {
  color:  yellowgreen;
}

继承的 text-shadow 属性在第一行片段上的使用值将是 yellowgreen。

测试

15. 序列化<color>

本节更新并替换了 CSS 对象模型中关于序列化 CSS 值部分中与序列化 <color> 值相关的内容。

在本节中,规范中使用的字符串及其对应的字符如下。

字符串 字符
" " U+0020 空格
"#" U+0023 数字符号
"," U+002C 逗号
"-" U+002D 连字符
"." U+002E 句号
"/" U+002F 斜线
"none" U+006E 小写字母 n
U+006F 小写字母 o
U+006E 小写字母 n
U+0065 小写字母 e

字符串 "." 应作为小数点分隔符使用,无论地区如何,且不应有千位分隔符。

对于支持缺少的颜色组件的语法形式, 值 none(等同于 NONE,nOnE 等)应以全小写的字符串“none”进行序列化。

15.1. 序列化 alpha 值

这适用于任何可以接受可选 alpha 值的<color>值。 这不适用于 opacity 属性。

如果在被限制到范围 [0, 1] 后,alpha 为 1,则在序列化中省略它;默认值为 1(完全不透明)。

如果 alpha 的值为 1 以外的任何值,则按照下面描述显式包含在序列化中。

如果值内部表示为 0 到 255 之间的整数(即 8 位无符号整数),请按以下步骤操作:

  1. alpha 为给定的整数。
  2. 如果存在一个 0 到 100 之间的整数,当它乘以 2.55 并四舍五入到最接近的整数(如果两个值同等接近则向上取整)时等于 alpha,则设 rounded 为该整数除以 100。
  3. 否则,将 rounded 设为 alpha 除以 0.255 并四舍五入到最接近的整数(如果两个值同等接近则向上取整),再除以 1000。
  4. rounded 序列化为<number> 的结果返回。

否则,返回给定值的序列化结果(作为 <number>,而不是 <percentage>)。

例如,如果 alpha 存储为 8 位无符号整数 237,整数 93 满足条件,因为 Math.round(93 * 2.55) 等于 237,因此 alpha 序列化为“0.93”。

但是,如果 alpha 存储为 8 位无符号整数 236,则不存在这样的整数(92 映射到 235,而 94 映射到 240),因此 236 ÷ 0.255 = 925.490196078,alpha 序列化为“0.92549”(不超过 6 位数字,去除尾随零)。

<number> 值以十进制表示,使用 "." 字符作为小数点分隔符。不得省略前导零,尾随零必须省略。

例如,alpha 值为 70% 时,将被序列化为字符串“0.7”,其中有小数点前的前导零,小数点分隔符为“.”(即使当前区域设置会使用其他字符,如“,”),且在“7”后的所有数字为“0”都被省略。

alpha 值保留的精度,以及序列化值中的小数位数,本规范中未定义,但必须至少足以进行整数百分比值的往返。因此,序列化值必须至少包含两位小数(除非已去除尾随零)。值必须向 +∞ 取整,而非截断。

例如,alpha 值为 12.3456789% 时,可以序列化为“0.12”或“0.123”或“0.1234”或“0.12346”(向 +∞ 取整,因为后续数字是 6)或任何相同形式的更长四舍五入序列化。

由于在解析时会对超出有效范围的 <alpha-value> 进行限制, 因此声明值将被限制。 然而,根据 CSS 值 4 § 10.12 范围检查,使用 calc() 指定的 <alpha-value> 在序列化指定形式时不会被限制; 但计算值会被限制。

例如,直接指定为 120% 的 alpha 值 会被序列化为字符串 "1"。 然而,如果它被指定为 calc(2*60%), 则声明值会被序列化为字符串 "calc(1.2)"。

15.2. 序列化 sRGB 值

以下 sRGB 值的序列化形式:

派生自声明值

当序列化由作者设置为 CSS 命名颜色系统颜色已弃用颜色transparent 的属性值时,因此对于声明值, 会保留 ASCII 小写关键字值。 对于计算值和使用值, 使用相应的 sRGB 值。

测试

因此,transparent 的序列化声明值是字符串 "transparent", 而 transparent 的序列化计算值是字符串 "rgba(0, 0, 0, 0)"。

对于所有其他 sRGB 值, 声明值、计算值和使用值 是相应的 sRGB 值。

在序列化期间, 任何缺失值 都会转换为 0。

15.2.1. sRGB 值的 HTML 兼容序列化

如果以下所有条件都为真:

  1. 色彩空间为 sRGB
  2. alpha 值为 1
  3. RGB 分量值在内部表示为 0 到 255(含)之间的整数(即 8 位无符号整数)
  4. 请求 HTML 兼容序列化

那么相应的 sRGB 值将按如下方式序列化为 6 位十六进制颜色表示法

一个由七个字符组成的字符串,包含字符“#”,紧随其后的是红色分量、绿色分量和蓝色分量的两位十六进制表示,顺序如此,使用 ASCII 小写十六进制数字。不允许有空格。

例如,填充样式设置为洋红色:
context.fillStyle = "rgb(255, 0, 255)"
console.log(context.fillStyle); // "#ff00ff"

色彩空间为 sRGB,每个分量的表示为 8 位, 数据格式不会产生 none 值,也不支持扩展范围值, 并且 alpha 值为 1。

HTML 兼容的序列化是字符串“#ff00ff”(而不是“#FF00FF”)。

否则,对于 sRGB,使用 sRGB 值的 CSS 序列化,对于其他色彩空间,使用 <color> 值的相关序列化

例如,在 CIE Lab 中,填充样式设置为深棕色:
context.fillStyle = "lab(29% 39 20)";
console.log(context.fillStyle); // "lab(29 39 20)"

CSS 序列化是字符串 "lab(29 39 20)"。

例如,填充样式设置为半透明洋红色:
context.fillStyle = "#ff00ffed";
console.log(context.fillStyle); // "rgba(255, 0, 255, 0.93)"

alpha 值不为 1,因此 CSS 序列化是字符串 "rgba(255, 0, 255, 0.93)"。

15.2.2. sRGB 值的 CSS 序列化

相应的 sRGB 值使用 rgb()rgba() 形式 (取决于(限制后的)alpha 值是否恰好为 1), 函数名称的所有字母均为ASCII 小写

为保持兼容性,sRGB 组件值以<number> 形式序列化,而不是 <percentage>。同样为了兼容性,组件值以十进制序列化,范围为 [0-255],不论它们存储的位深度如何。

如前所述,单位 alpha 值不会显式序列化。此外,为了保持兼容性,如果 alpha 恰好为 1,则使用 rgb() 形式,隐式表示 alpha;否则使用 rgba() 形式,显式表示 alpha 值。

为了保持兼容性,使用带逗号分隔符的传统形式;每个逗号后面跟一个 ASCII 空格。这包括用于分隔 rgba() 中蓝色分量和 alpha 值的逗号(而不是斜线)。

例如,以下值的序列化形式为:

 rgb(29 164 192 / 95%)

是字符串 "rgba(29, 164, 192, 0.95)"

例如,一个作者提供的值:
 hwb(740deg 20% 30% / 50%)

首先标准化为:

 hwb(20 20% 30% / 50%)

然后转换为 sRGB 并序列化为:

 rgba(178.5, 93.5, 51, 0.5)

返回结果的精度如下所述

注意: 与 CSS Color 3 相反,rgb() 函数的参数是 <number> 类型,而不是 <integer>。因此,任何高于八位精度的内容都用小数部分表示。

保留 sRGB 组件值的精度,即序列化值中的有效数字位数,本规范中未定义,但至少必须足以进行八位值的往返。值必须向 +∞ 取整,而非截断。

注意: 期望从 getComputedStyle 返回的颜色值为 <integer> 组件值的脚本编写者,建议更新它们以支持 <number> 值。

例如:

 rgb(146.064 107.457 131.223)

现在是有效的,等同于:

 rgb(57.28% 42.14% 51.46%)

符合规范的序列化形式为字符串 "rgb(146.06, 107.46, 131.2)"。

任何组件值中的尾随零必须被省略;如果小数部分由全零组成,也必须省略小数点。这意味着以整数组件值指定的 sRGB 颜色将以向后兼容的整数值序列化。

以下颜色的序列化计算值:

 ''goldenrod''

为字符串“rgb(218, 165, 32)”而不是字符串“rgb(218.000, 165.000, 32.000)”

15.3. 序列化 Lab 和 LCH 值

lch()lab() 值的序列化形式 派生自计算值,并使用 lab()lch() 形式, 函数名称使用ASCII 小写字母。

分量值以十进制序列化; L、a、b 和 C 分量值 序列化为 <number>, 酌情使用 Lab 百分比参考范围LCH 百分比参考范围 执行百分比到数字的转换; 因此 0% L 映射到 0, 100% L 映射到 100。 必须使用单个 ASCII 空格字符 " " 作为分量值之间的分隔符。

测试

以下值的序列化形式为:

 lab(56.200% 0.000 83.600)

字符串 "lab(56.2 0 83.6)"

以下值的序列化形式为:

 lab(56.200% 0.000 66.88%)

字符串 "lab(56.2 0 83.6)"

任何组件值中的尾随小数部分零必须省略;如果小数部分全为零,小数点也必须省略。

以下值的序列化形式为:

 lch(37% 105.0 305.00)

字符串 "lch(37 105 305)",而不是 "lch(37 105.0 305.00)"。

保留 lab() 组件值的精度,即序列化值中的有效数字位数,本规范未定义,但由于广色域,必须足够以进行 0 到 100 之间的 L 值以及 ±127 之间的 a 和 b 值的往返操作,至少需要 16 位精度;这将导致至少有三位小数,除非尾随零已被省略。(建议内部存储为半浮点数或浮点数)。值必须 向 +∞ 取整,而非截断。

注意: 超宽色域空间中的 a 和 b 值可以超出 ±125。例如,所有prophoto-rgb 主色和次色都超过了此范围,但在 ±200 之内。

如前所述,单位 alpha 值不会显式序列化。非单位 alpha 值必须显式序列化,并且字符串 " / "(一个 ASCII 空格,然后斜杠,然后另一个空格)必须用于将 b 组件值与 alpha 值分隔开。

以下值的序列化形式为:

 lch(56.2% 83.6 357.4 /93%)

字符串 "lch(56.2 83.6 357.4 / 0.93)",而不是 "lch(56.2% 83.6 357.4 / 0.93)"

15.4. 序列化 Oklab 和 OKLCh 值

oklch()oklab() 值的序列化形式 派生自计算值,并使用 oklab()oklch() 形式, 函数名称使用ASCII 小写字母。

分量值以十进制序列化; L、a、b 和 C 分量值 序列化为 <number>, 酌情使用 Oklab 百分比参考范围OKLCh 百分比参考范围 执行百分比到数字的转换; 因此 0% L 映射到 0, 100% L 映射到 1.0。 必须使用单个 ASCII 空格字符 " " 作为分量值之间的分隔符。

测试

以下值的序列化形式为:

 oklab(54.0% -0.10 -0.02)

字符串 "oklab(0.54 -0.1 -0.02)",而不是 "oklab(54 -0.1 -0.02)" 或 "oklab(54% -0.1 -0.02)"

以下值的序列化形式为:

 oklab(54.0 -25% -5%)

字符串 "oklab(0.54 -0.1 -0.02)",而不是 "oklab(54 -0.25 -0.05)"

任何组件值中的尾随小数部分零必须省略;如果小数部分全为零,小数点也必须省略。

以下值的序列化形式为:

 oklch(56.43% 0.0900 123.40)

字符串 "oklch(0.5643 0.09 123.4)",而不是 "oklch(0.5643 0.0900 123.40)"。

保留 oklab() 组件值的精度,即序列化值中的有效数字位数,本规范未定义,但由于广色域,必须足够以进行 0 到 1(0% 到 100%)之间的 L 值,以及 ±0.5 之间的 a、b 和 C 值的往返操作,至少需要 16 位精度;这将导致至少有五位小数,除非尾随零已被省略。(建议内部存储为半浮点数或浮点数)。值必须 向 +∞ 取整,而非截断。

注意: 超宽色域空间中的 a、b 和 C 值可以超出 ±0.5。例如,prophoto-rgb 的绿色和蓝色主色的 C 值分别为 0.526 和 1.413,超出了该范围。

如前所述,单位 alpha 值不会显式序列化。非单位 alpha 值必须显式序列化,并且字符串 " / "(一个 ASCII 空格,然后斜杠,然后另一个空格)必须用于将最终的颜色组件值(b 或 C)与 alpha 值分隔开。

以下值的序列化形式为:

 oklch(53.85% 0.1725 320.67 / 70%)

字符串 "oklch(0.5385 0.1725 320.67 / 0.7)"

15.5. 序列化 color() 函数的值

color() 值的序列化形式来源于 计算值,并使用 color() 形式,函数名和色彩空间名中的字母使用 ASCII 小写

组件值以十进制形式序列化,作为 <number>。各组件值之间,以及色彩空间名和第一个颜色组件之间,必须使用单个 ASCII 空格字符 " " 作为分隔符。

测试

以下值的序列化形式为:

 color(dIsPlAy-P3  0.964  0.763  0.787)

字符串 "color(display-p3 0.96 0.76 0.79)",如果保留两位小数。注意 0.787 四舍五入为 0.79,而不是截断到 0.78。

任何组件值中的尾随小数部分零必须省略;如果小数部分全为零,小数点也必须省略。

以下值的序列化形式为:

 color(rec2020 0.400 0.660 0.340)

字符串 "color(rec2020 0.4 0.66 0.34)",而不是 "color(rec2020 0.400 0.660 0.340)"。

如果色彩空间为 sRGB,则在序列化结果中仍需显式指定色彩空间。

对于预定义的色彩空间,往返所需的最低精度如下:

色彩空间 最小位数
srgb 10
srgb-linear 12
display-p3 10
a98-rgb 10
prophoto-rgb 12
rec2020 12
xyzxyz-d50xyz-d65 16

(推荐内部存储为每个组件 16 位,半浮点或浮点)。值必须 向 +∞ 取整,而非截断。

注意: 与传统形式如 rgb()hsl() 等相比,color(srgb) 有更高的最小精度要求。因此,鼓励希望获得更高精度的样式表作者使用 color(srgb) 形式。

如前所述,单位 alpha 值不会显式序列化。非单位 alpha 值必须显式序列化,并且字符串 " / "(一个 ASCII 空格,然后斜杠,然后另一个空格)必须用于将最终的颜色组件值与 alpha 值分隔开。

以下值的序列化形式为:

 color(prophoto-rgb 0.2804 0.40283 0.42259/85%)

字符串 "color(prophoto-rgb 0.28 0.403 0.423 / 0.85)",如果保留三位小数。

15.6. 序列化其他颜色

这适用于 currentcolor

该值的序列化形式来源于 计算值,并且颜色名称中的字母使用 ASCII 小写

currentColor 的序列化形式为字符串 "currentcolor"。

16. 序列化 <opacity-value>

这适用于 opacity 属性。

指定的给定不透明度值序列化为 <number>,而不是 <percentage>

<number> 值以十进制表示, 小数点分隔符为 "." 字符。 前导零不得省略。 尾随零必须省略。

超出 [0,1] 范围的不透明度值 在序列化的指定值中保留,不进行限制。

保留不透明度值的精度, 以及序列化值中的小数位数, 本规范未定义, 但必须至少足以 往返整数百分比值。 因此,序列化值必须包含 至少两位小数 (除非已删除尾随零)。 值必须向 +∞ 四舍五入,而不是截断。

17. 默认样式规则

以下样式表是说明性的,而非规范性的。此样式表可作为实现 HTML 文档默认样式的一部分。

/* 桌面用户代理的传统超链接颜色 */
:link { color: LinkText; }
:visited { color: VisitedText; }
:active { color: ActiveText; }

18. 颜色转换的示例代码

本节不是规范性的。

测试

本节不是规范性的,因此不需要测试。


为了更清晰地表达,使用了一个库进行矩阵乘法。(这比内联所有的乘法和加法更易读)。矩阵采用列主序排列。

// 颜色转换的示例代码
// 转换也可以使用 ICC 配置文件和颜色管理系统完成
// 为了清晰起见,使用了一个库来进行矩阵乘法(multiply-matrices.js)

// 标准白点,由 4 位数 CIE x,y 色度定义
const D50 = [0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585];
const D65 = [0.3127 / 0.3290, 1.00000, (1.0 - 0.3127 - 0.3290) / 0.3290];

// 与 sRGB 相关的函数

function lin_sRGB(RGB) {
	// 将一组 sRGB 值转换为
	// 其中在色域内的值在范围 [0 - 1] 内
	// 转换为线性光(未压缩)形式。
	// https://en.wikipedia.org/wiki/SRGB
	// 扩展的传递函数:
	// 对于负值,线性部分沿轴反射进行扩展,
	// 然后使用反射的幂函数。
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs <= 0.04045) {
			return val / 12.92;
		}

		return sign * (Math.pow((abs + 0.055) / 1.055, 2.4));
	});
}

function gam_sRGB(RGB) {
	// 将线性光 sRGB 值在范围 0.0-1.0 内转换为
	// 伽马校正的形式
	// https://en.wikipedia.org/wiki/SRGB
	// 扩展的传递函数:
	// 对于负值,线性部分沿轴反射延伸,
	// 然后使用反射的幂函数
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs > 0.0031308) {
			return sign * (1.055 * Math.pow(abs, 1/2.4) - 0.055);
		}

		return 12.92 * val;
	});
}

function lin_sRGB_to_XYZ(rgb) {
	// 将线性光 sRGB 值数组转换为 CIE XYZ
	// 使用 sRGB 自身的白点 D65(无色度适配)

	var M = [
		[ 506752 / 1228815,  87881 / 245763,   12673 /   70218 ],
		[  87098 /  409605, 175762 / 245763,   12673 /  175545 ],
		[   7918 /  409605,  87881 / 737289, 1001167 / 1053270 ],
	];
	return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_sRGB(XYZ) {
	// 将 XYZ 转换为线性光 sRGB

	var M = [
		[   12831 /   3959,    -329 /    214, -1974 /   3959 ],
		[ -851781 / 878810, 1648619 / 878810, 36519 / 878810 ],
		[     705 /  12673,   -2585 /  12673,   705 /    667 ],
	];

	return multiplyMatrices(M, XYZ);
}

// 与 display-p3 相关的函数


function lin_P3(RGB) {
	// 将 display-p3 RGB 值数组在范围 0.0 - 1.0 内
	// 转换为线性光(未压缩)形式。

	return lin_sRGB(RGB);	// 与 sRGB 相同
}

function gam_P3(RGB) {
	// 将线性光 display-p3 RGB 值数组在范围 0.0-1.0 内转换为
	// 伽马校正的形式

	return gam_sRGB(RGB);	// 与 sRGB 相同
}

function lin_P3_to_XYZ(rgb) {
	// 将线性光 display-p3 值数组转换为 CIE XYZ
	// 使用 D65(无色度适配)
	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
	var M = [
		[ 608311 / 1250200, 189793 / 714400,  198249 / 1000160 ],
		[  35783 /  156275, 247089 / 357200,  198249 / 2500400 ],
		[      0 /       1,  32229 / 714400, 5220557 / 5000800 ],
	];

	return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_P3(XYZ) {
	// 将 XYZ 转换为线性光 P3
	var M = [
		[ 446124 / 178915, -333277 / 357830, -72051 / 178915 ],
		[ -14852 /  17905,   63121 /  35810,    423 /  17905 ],
		[  11844 / 330415,  -50337 / 660830, 316169 / 330415 ],
	];

	return multiplyMatrices(M, XYZ);
}

// prophoto-rgb 函数

function lin_ProPhoto(RGB) {
	// 将一组 prophoto-rgb 值转换为
	// 其中在色域内的颜色在范围 [0.0 - 1.0] 内
	// 转换为线性光(未压缩)形式。
	// 传递曲线为 gamma 1.8,具有一个小的线性部分
	// 扩展的传递函数
	const Et2 = 16/512;
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs <= Et2) {
			return val / 16;
		}

		return sign * Math.pow(abs, 1.8);
	});
}

function gam_ProPhoto(RGB) {
	// 将一组线性光 prophoto-rgb 值(范围 0.0-1.0)
	// 转换为伽马校正形式
	// 传递曲线为 gamma 1.8,具有一个小的线性部分
	// TODO 对于负值,在轴的反射上扩展线性部分,然后在其下方添加幂次
	const Et = 1/512;
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs >= Et) {
			return sign * Math.pow(abs, 1/1.8);
		}

		return 16 * val;
	});
}

function lin_ProPhoto_to_XYZ(rgb) {
	// 将一组线性光 prophoto-rgb 值转换为 CIE D50 XYZ
	// 矩阵无法用有理形式表示,但计算精度达 64 位
	// 参见 https://github.com/w3c/csswg-drafts/issues/7675
	var M = [
		[ 0.79776664490064230,  0.13518129740053308,  0.03134773412839220 ],
		[ 0.28807482881940130,  0.71183523424187300,  0.00008993693872564 ],
		[ 0.00000000000000000,  0.00000000000000000,  0.82510460251046020 ]
	];

	return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_ProPhoto(XYZ) {
	// 将 D50 XYZ 转换为线性光 prophoto-rgb
	var M = [
		[  1.34578688164715830, -0.25557208737979464, -0.05110186497554526 ],
        [ -0.54463070512490190,  1.50824774284514680,  0.02052744743642139 ],
        [  0.00000000000000000,  0.00000000000000000,  1.21196754563894520 ]
	];

	return multiplyMatrices(M, XYZ);
}

// a98-rgb 函数

function lin_a98rgb(RGB) {
	// 将一个范围在 0.0 - 1.0 的 a98-rgb 值数组转换为线性光(未压缩)形式。
	// 现在也接受负值。
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

	  	return sign * Math.pow(abs, 563/256);
	});
}

function gam_a98rgb(RGB) {
	// 将一个线性光 a98-rgb 值数组(范围在 0.0-1.0)转换为伽马校正形式。
	// 现在也接受负值。
	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		return sign * Math.pow(abs, 256/563);
	});
}

function lin_a98rgb_to_XYZ(rgb) {
	// 将线性光 a98-rgb 值数组转换为 CIE XYZ。
	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
	// 相较于 https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf 第 4.3.5.3 节有更高的数值精度。
	// 以下值是根据 R G B W 的色度坐标从原理出发计算得出的。
	// 参见 matrixmaker.html。
	var M = [
		[ 573536 /  994567,  263643 / 1420810,  187206 /  994567 ],
		[ 591459 / 1989134, 6239551 / 9945670,  374412 / 4972835 ],
		[  53769 / 1989134,  351524 / 4972835, 4929758 / 4972835 ],
	];

	return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_a98rgb(XYZ) {
	// 将 CIE XYZ 转换为线性光 a98-rgb。
	var M = [
		[ 1829569 /  896150, -506331 /  896150, -308931 /  896150 ],
		[ -851781 /  878810, 1648619 /  878810,   36519 /  878810 ],
		[   16779 / 1248040, -147721 / 1248040, 1266979 / 1248040 ],
	];

	return multiplyMatrices(M, XYZ);
}

// Rec. 2020 相关函数

function lin_2020(RGB) {
	// 将一个范围在 0.0 - 1.0 的 Rec.2020 RGB 值数组转换为线性光(未压缩)形式。
	// ITU-R BT.2020-2 第 4 页。

	const α = 1.09929682680944 ;
	const β = 0.018053968510807;

	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs < β * 4.5 ) {
			return val / 4.5;
		}

		return sign * (Math.pow((abs + α -1 ) / α, 1/0.45));
	});
}

function gam_2020(RGB) {
	// 将一个线性光 Rec.2020 RGB 值数组(范围在 0.0-1.0)转换为伽马校正形式。
	// ITU-R BT.2020-2 第 4 页。

	const α = 1.09929682680944 ;
	const β = 0.018053968510807;


	return RGB.map(function (val) {
		let sign = val < 0? -1 : 1;
		let abs = Math.abs(val);

		if (abs > β ) {
			return sign * (α * Math.pow(abs, 0.45) - (α - 1));
		}

		return 4.5 * val;
	});
}

function lin_2020_to_XYZ(rgb) {
	// 将一个线性光 Rec.2020 值数组转换为 CIE XYZ,使用 D65(无色度适应)。
	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
	var M = [
		[ 63426534 / 99577255,  20160776 / 139408157,  47086771 / 278816314 ],
		[ 26158966 / 99577255, 472592308 / 697040785,   8267143 / 139408157 ],
		[        0 /        1,  19567812 / 697040785, 295819943 / 278816314 ],
	];
	// 实际计算的 0 值为 4.994106574466076e-17

	return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_2020(XYZ) {
	// 将 CIE XYZ 转换为线性光 Rec.2020。
	var M = [
		[  30757411 / 17917100, -6372589 / 17917100, -4539589 / 17917100 ],
		[ -19765991 / 29648200, 47925759 / 29648200,   467509 / 29648200 ],
		[    792561 / 44930125, -1921689 / 44930125, 42328811 / 44930125 ],
	];

	return multiplyMatrices(M, XYZ);
}

// 色度适应

function D65_to_D50(XYZ) {
	// 从 D65 到 D50 的 Bradford 色度适应。
	// 下方矩阵是三次操作的结果:
	// - 从 XYZ 转换为视网膜锥体域。
	// - 根据不同参考白点调整各成分比例。
	// - 转换回 XYZ。
	// 参见 https://github.com/LeaVerou/color.js/pull/354/files
	
	var M =  [
		[  1.0479297925449969,    0.022946870601609652,  -0.05019226628920524  ],
		[  0.02962780877005599,   0.9904344267538799,    -0.017073799063418826 ],
		[ -0.009243040646204504,  0.015055191490298152,   0.7518742814281371   ]
	];

	return multiplyMatrices(M, XYZ);
}

function D50_to_D65(XYZ) {
	// 从 D50 到 D65 的 Bradford 色度适应。
	// 参见 https://github.com/LeaVerou/color.js/pull/360/files
	var M = [
		[  0.955473421488075,    -0.02309845494876471,   0.06325924320057072  ],
		[ -0.0283697093338637,    1.0099953980813041,    0.021041441191917323 ],
		[  0.012314014864481998, -0.020507649298898964,  1.330365926242124    ]
	];

	return multiplyMatrices(M, XYZ);
}

// CIE Lab 和 LCH

function XYZ_to_Lab(XYZ) {
	// 假设 XYZ 是相对于 D50 的,转换为 CIE Lab。
	// 基于 CIE 标准,该标准现在将这些定义为有理分数。
	var ε = 216/24389;  // 6^3/29^3
	var κ = 24389/27;   // 29^3/3^3

	// 计算 xyz,即相对于参考白点缩放后的 XYZ。
	var xyz = XYZ.map((value, i) => value / D50[i]);

	// 现在计算 f。
	var f = xyz.map(value => value > ε ? Math.cbrt(value) : (κ * value + 16)/116);

	return [
		(116 * f[1]) - 16, 	 // L
		500 * (f[0] - f[1]), // a
		200 * (f[1] - f[2])  // b
	];
	// L 的范围为 [0,100]。在 CSS 中使用时,加上百分号。
}
function Lab_to_XYZ(Lab) {
	// 将 Lab 转换为 D50 适配的 XYZ
	// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
	var κ = 24389/27;   // 29^3/3^3
	var ε = 216/24389;  // 6^3/29^3
	var f = [];

	// 计算 f,从与亮度相关的项开始
	f[1] = (Lab[0] + 16)/116;
	f[0] = Lab[1]/500 + f[1];
	f[2] = f[1] - Lab[2]/200;

	// 计算 xyz
	var xyz = [
		Math.pow(f[0],3) > ε ?   Math.pow(f[0],3)            : (116*f[0]-16)/κ,
		Lab[0] > κ * ε ?         Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ,
		Math.pow(f[2],3)  > ε ?  Math.pow(f[2],3)            : (116*f[2]-16)/κ
	];

	// 通过参考白点缩放 xyz 来计算 XYZ
	return xyz.map((value, i) => value * D50[i]);
}


function Lab_to_LCH(Lab) {
	var epsilon = 0.0015;
	var chroma = Math.sqrt(Math.pow(Lab[1], 2) + Math.pow(Lab[2], 2)); // Chroma
	var hue = Math.atan2(Lab[2], Lab[1]) * 180 / Math.PI;
	if (hue < 0) {
		hue = hue + 360;
	}
	if (chroma <= epsilon) {
		hue = NaN;
	}
	return [
		Lab[0], // L is still L
		chroma, // Chroma
		hue // Hue, in degrees [0 to 360)
	];
}

function LCH_to_Lab(LCH) {
	// 从极坐标形式转换回
	return [
		LCH[0], // L 仍然是 L
		LCH[1] * Math.cos(LCH[2] * Math.PI / 180), // a
		LCH[1] * Math.sin(LCH[2] * Math.PI / 180) // b
	];
}

// OKLab 和 OKLCH
// https://bottosson.github.io/posts/oklab/

// XYZ <-> LMS 矩阵重新计算以保持一致的参考白点
// 详见 https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484
// 重新计算为 64 位精度
// 详见 https://github.com/color-js/color.js/pull/357

function XYZ_to_OKLab(XYZ) {
	// 给定相对于 D65 的 XYZ,转换为 OKLab
	var XYZtoLMS = [
		[ 0.8190224379967030, 0.3619062600528904, -0.1288737815209879 ],
		[ 0.0329836539323885, 0.9292868615863434,  0.0361446663506424 ],
		[ 0.0481771893596242, 0.2642395317527308,  0.6335478284694309 ]
	];
	var LMStoOKLab = [
		[ 0.2104542683093140,  0.7936177747023054, -0.0040720430116193 ],
		[ 1.9779985324311684, -2.4285922420485799,  0.4505937096174110 ],
		[ 0.0259040424655478,  0.7827717124575296, -0.8086757549230774 ]
	];

	var LMS = multiplyMatrices(XYZtoLMS, XYZ);
	// JavaScript 的 Math.cbrt 返回的是符号匹配的立方根
    // 如果移植到其他语言,请小心
    // 特别是如果使用通用的幂函数时
    return multiplyMatrices(LMStoOKLab, LMS.map(c => Math.cbrt(c)));
    // L 的范围是 [0,1]。用于 CSS 时,乘以 100 并加上百分号
}

function OKLab_to_XYZ(OKLab) {
	// 给定 OKLab,将其转换为相对于 D65 的 XYZ
	var LMStoXYZ =  [
		[  1.2268798758459243, -0.5578149944602171,  0.2813910456659647 ],
		[ -0.0405757452148008,  1.1122868032803170, -0.0717110580655164 ],
		[ -0.0763729366746601, -0.4214933324022432,  1.5869240198367816 ]
	];
	var OKLabtoLMS = [
		[ 1.0000000000000000,  0.3963377773761749,  0.2158037573099136 ],
		[ 1.0000000000000000, -0.1055613458156586, -0.0638541728258133 ],
		[ 1.0000000000000000, -0.0894841775298119, -1.2914855480194092 ]
    ];

	var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab);
	return multiplyMatrices(LMStoXYZ, LMSnl.map(c => c ** 3));
}

function OKLab_to_OKLCH(OKLab) {
	var epsilon = 0.000004;
	var hue = Math.atan2(OKLab[2], OKLab[1]) * 180 / Math.PI;
	var chroma = Math.sqrt(OKLab[1] ** 2 + OKLab[2] ** 2);
	if (hue < 0) {
		hue = hue + 360;
	}
	if (chroma <= epsilon) {
		hue = NaN;
	}
	return [
		OKLab[0], // L is still L
		chroma,
		hue
	];
}

function OKLCH_to_OKLab(OKLCH) {
	return [
		OKLCH[0], // L is still L
		OKLCH[1] * Math.cos(OKLCH[2] * Math.PI / 180), // a
		OKLCH[1] * Math.sin(OKLCH[2] * Math.PI / 180)  // b
	];
}

// 预乘透明度转换

function rectangular_premultiply(color, alpha) {
// 给定一个矩形正交色彩空间中的颜色
// 以及一个 alpha 值
// 返回预乘后的形式
	return color.map((c) => c * alpha)
}

function rectangular_un_premultiply(color, alpha) {
// 给定一个预乘后的矩形正交色彩空间中的颜色
// 以及一个 alpha 值
// 返回实际的颜色
	if (alpha === 0) {
		return color; // 避免除以零
	}
	return color.map((c) => c / alpha)
}

function polar_premultiply(color, alpha, hueIndex) {
	// 给定一个柱状极坐标色彩空间中的颜色
	// 以及一个 alpha 值
	// 返回预乘后的形式
	// hueIndex 表示颜色数组中的哪个条目对应色调角度
	// 例如在 OKLCH 中是 2
	// 而在 HSL 中是 0
	return color.map((c, i) => c * (hueIndex === i? 1 : alpha))
}

function polar_un_premultiply(color, alpha, hueIndex) {
	// 给定一个柱状极坐标色彩空间中的颜色
	// 以及一个 alpha 值
	// 返回实际的颜色
	// hueIndex 表示颜色数组中的哪个条目对应色调角度
	// 例如在 OKLCH 中是 2
	// 而在 HSL 中是 0
	if (alpha === 0) {
		return color; // 避免除以零
	}
	return color.map((c, i) => c / (hueIndex === i? 1 : alpha))
}

// 可以轻松定义便捷函数,例如
function hsl_premultiply(color, alpha) {
	return polar_premultiply(color, alpha, 0);
}

19. ΔE2000和ΔEOK颜色差异的示例代码

本节非规范性。

测试

本节非规范性,不需要测试。


19.1. ΔE2000

最简单的颜色差异指标,ΔE76, 仅仅是在Lab颜色空间中的欧几里得距离。 虽然这作为一个初步近似是可以接受的, 但如印刷和织物染色等对颜色要求较高的行业 很快就开发出了改进的公式。 目前最广泛使用的公式是ΔE2000。 它修正了与ΔE76相比的一些已知的非对称性和非线性。 由于公式复杂, 并且对各种中间计算的符号依赖性很强, 实现常常出错 [Sharma]

下面的示例代码已通过验证,具有五个有效数字, 使用由[Sharma]发布的成对Lab值和期望的ΔE2000测试套件, 并且是正确的。

// deltaE2000 是对 deltaE76 和 deltaE94 的统计学显著改进
// 并且推荐由 CIE 和 Idealliance 使用
// 尤其适用于颜色差异小于 10 deltaE76 的情况
// 但是它非常复杂
// 许多实现存在小错误!

/**
 * @param {number[]} reference - CIE Lab 值数组:L 为 0..100,a 和 b 大约为 -150..150
 * @param {number[]} sample - CIE Lab 值数组:L 为 0..100,a 和 b 大约为 -150..150
 * @return {number} 样本颜色与参考颜色的差异程度
 */

function deltaE2000 (reference, sample) {

    // 给定参考色和样本色,
    // 都在 CIE Lab 中,
    // 计算 deltaE 2000。

    // 此实现假定参数加权因子 kL, kC 和 kH
    // (用于观看条件的影响)
    // 都为 1,这也是常见的情况。

    let [L1, a1, b1] = reference;
    let [L2, a2, b2] = sample;
    let C1 = Math.sqrt(a1 ** 2 + b1 ** 2);
    let C2 = Math.sqrt(a2 ** 2 + b2 ** 2);

	let Cbar = (C1 + C2)/2; // 平均色度

	// 从平均色度计算 a 轴不对称因子
	// 使得接近中性的颜色的 JND 椭圆变为圆形

	let C7 = Math.pow(Cbar, 7);
	const Gfactor = Math.pow(25, 7);
	let G = 0.5 * (1 - Math.sqrt(C7/(C7+Gfactor)));

	// 通过不对称因子缩放 a 轴

	let adash1 = (1 + G) * a1;
	let adash2 = (1 + G) * a2;

	// 从缩放后的 a 和原始 b 轴计算新的色度

	let Cdash1 = Math.sqrt(adash1 ** 2 + b1 ** 2);
	let Cdash2 = Math.sqrt(adash2 ** 2 + b2 ** 2);

	// 计算新的色相,如果是真正的中性色则色相为 0

	const π = Math.PI;
	const r2d = 180 / π;
	const d2r = π / 180;
	let h1 = (adash1 === 0 && b1 === 0)? 0: Math.atan2(b1, adash1);
	let h2 = (adash2 === 0 && b2 === 0)? 0: Math.atan2(b2, adash2);

	if (h1 < 0) {
		h1 += 2 * π;
	}
	if (h2 < 0) {
		h2 += 2 * π;
	}

	h1 *= r2d;
	h2 *= r2d;

	// 亮度和色度的差异,符号很重要
	let ΔL = L2 - L1;
	let ΔC = Cdash2 - Cdash1;

	// 色相差异,注意获取正确的符号
	let hdiff = h2 - h1;
	let hsum = h1 + h2;
	let habs = Math.abs(hdiff);
	let Δh;

	if (Cdash1 * Cdash2 === 0) {
		Δh = 0;
	}
	else if (habs <= 180) {
		Δh = hdiff;
	}
	else if (hdiff > 180) {
		Δh = hdiff - 360;
	}
	else if (hdiff < -180) {
		Δh = hdiff + 360;
	}
	else {
		console.log("the unthinkable has happened");
	}

		
	let ΔH = 2 * Math.sqrt(Cdash2 * Cdash1) * Math.sin(Δh * d2r / 2);

	
	let Ldash = (L1 + L2)/2;
	let Cdash = (Cdash1 + Cdash2)/2;
	let Cdash7 = Math.pow(Cdash, 7);

	
	
	let hdash;
	if (Cdash1 == 0 && Cdash2 == 0) {
		hdash = hsum;   
	}
	else if (habs <= 180) {
		hdash = hsum / 2;
	}
	else if (hsum < 360) {
		hdash = (hsum + 360) / 2;
	}
	else {
		hdash = (hsum - 360) / 2;
	}

	
	

	
	let lsq = (Ldash - 50) ** 2;
	let SL = 1 + ((0.015 * lsq) / Math.sqrt(20 + lsq));

	
	let SC = 1 + 0.045 * Cdash;

	
	let T = 1;
	T -= (0.17 * Math.cos((     hdash - 30)  * d2r));
	T += (0.24 * Math.cos(  2 * hdash        * d2r));
	T += (0.32 * Math.cos(((3 * hdash) + 6)  * d2r));
	T -= (0.20 * Math.cos(((4 * hdash) - 63) * d2r));

	
	let SH = 1 + 0.015 * Cdash * T;

	
	
	let Δθ = 30 * Math.exp(-1 * (((hdash - 275)/25) ** 2));
	let RC = 2 * Math.sqrt(Cdash7/(Cdash7 + Gfactor));
	let RT = -1 * Math.sin(2 * Δθ * d2r) * RC;

	
	let dE = (ΔL / SL) ** 2;
	dE += (ΔC / SC) ** 2;
	dE += (ΔH / SH) ** 2;
	dE += RT * (ΔC / SC) * (ΔH / SH);
	return Math.sqrt(dE);
	// Yay!!!
};

19.2. ΔEOK

由于 Oklab 不存在 CIE Lab 的色相线性、色相均匀性和色度非线性的问题,因此颜色差异度量不需要对它们进行修正,因此在 Oklab 颜色空间中的颜色差异度量就是简单的欧几里得距离。

// 计算 deltaE OK
// 简单的平方和的平方根
/**
 * @param {number[]} reference - OKLab 值数组:L 范围 0..1,a 和 b 范围 -1..1
 * @param {number[]} sample - OKLab 值数组:L 范围 0..1,a 和 b 范围 -1..1
 * @return {number} 样本颜色与参考颜色的差异度
 */
function deltaEOK (reference, sample) {
    let [L1, a1, b1] = reference; 
    let [L2, a2, b2] = sample; 
    let ΔL = L1 - L2; 
    let Δa = a1 - a2; 
    let Δb = b1 - b2; 
    return Math.sqrt(ΔL ** 2 + Δa ** 2 + Δb ** 2); 
}

附录 A:已废弃的 CSS 系统颜色

CSS 的早期版本定义了几个额外的系统颜色。然而,这些颜色关键字已经被废弃,因为它们对于原来的用途(使网站元素看起来像它们的本地操作系统对应部分)已经不够充分,并且会增加安全风险,容易使网页“伪装”成本地操作系统对话框,从而增加指纹识别面,损害用户隐私。

用户代理必须支持这些关键字,并且为了减轻指纹识别,必须将它们映射到下方列出的(未废弃的)系统颜色作者不得使用这些关键字。

已废弃的系统颜色表示为<deprecated-color>子类型,并定义为:

ActiveBorder
活动窗口边框。与ButtonBorder相同。
ActiveCaption
活动窗口标题。与Canvas相同。
AppWorkspace
多文档界面的背景色。与Canvas相同。
Background
桌面背景。与Canvas相同。
ButtonHighlight
面对光源的 3D 元素边框的颜色,使这些元素通过一层围绕的边框看起来具有 3D 效果。与ButtonFace相同。
ButtonShadow
远离光源的 3D 元素边框的颜色,使这些元素通过一层围绕的边框看起来具有 3D 效果。与ButtonFace相同。
CaptionText
标题、大小框和滚动条箭头框中的文本。与CanvasText相同。
InactiveBorder
非活动窗口边框。与ButtonBorder相同。
InactiveCaption
非活动窗口标题。与Canvas相同。
InactiveCaptionText
非活动标题中的文本颜色。与GrayText相同。
InfoBackground
工具提示控件的背景色。与Canvas相同。
InfoText
工具提示控件的文本颜色。与CanvasText相同。
Menu
菜单背景。与Canvas相同。
MenuText
菜单中的文本。与CanvasText相同。
Scrollbar
滚动条的灰色区域。与Canvas相同。
ThreeDDarkShadow
3D 元素远离光源的两条边框中较暗的一条(通常是外层边框)的颜色,使这些元素看起来具有 3D 效果。与ButtonBorder相同。
ThreeDFace
3D 元素的表面背景颜色,这些元素通过两层同心边框看起来具有 3D 效果。与ButtonFace相同。
ThreeDHighlight
3D 元素面对光源的两条边框中较亮的一条(通常是外层边框)的颜色,使这些元素看起来具有 3D 效果。与ButtonBorder相同。
ThreeDLightShadow
3D 元素面对光源的两条边框中较暗的一条(通常是内层边框)的颜色,使这些元素看起来具有 3D 效果。与ButtonBorder相同。
ThreeDShadow
3D 元素远离光源的两条边框中较亮的一条(通常是内层边框)的颜色,使这些元素看起来具有 3D 效果。与ButtonBorder相同。
Window
窗口背景。与Canvas相同。
WindowFrame
窗口框架。与ButtonBorder相同。
WindowText
窗口中的文本。与CanvasText相同。
测试

附录 B:已废弃的古怪十六进制颜色

当 CSS 在怪癖模式下解析时,<quirky-color>是一种仅在某些属性中有效的<color>类型:

它在包含或引用这些属性的属性中无效,例如background简写属性,或在函数表示法color-mix()

此外,虽然<quirky-color>在解析受影响的属性时必须作为<color>有效,但在@supports规则中,它在CSS.supports()方法中无效

<quirky-color>可以表示为<number-token><dimension-token>,或<ident-token>,根据以下规则:

(换句话说,怪癖模式允许在没有前导“#”的情况下编写十六进制颜色,但具有奇怪的解析规则。)

测试

奇特的十六进制颜色


致谢

除了那些为 CSS Color 3 做出贡献的人之外,编辑们还要感谢 Emilio Cobos Álvarez、Alexey Ardov、Chris Bai、Amelia Bellamy-Royds、Lars Borg、Mike Bremford、Andreu Botella、Dan Burzo、Max Derhak、fantasai、Simon Fraser、Devon Govett、Phil Green、Dean Jackson、Andreas Kraushaar、Pierre-Anthony Lemieux、Tiaan Louw、Cameron McCormack、Romain Menke、Chris Murphy、Isaac Muse、 Jonathan Neal、Chris Needham、Björn Ottosson、Christoph Päper、Brad Pettit、Xidorn Quan、Craig Revie、 Melanie Richards、Florian Rivoal、Jacob Rus、Joseph Salowey、Simon Sapin、Igor Snitkin、Lea Verou、Mark Watson、James Stuckey Weber、Sam Weinig 和 Natalie Weizenbaum。

更改记录

2024 年 2 月 13 日候选推荐草案以来的变更

自 2022 年 11 月 1 日的候选推荐草案以来的更改

自 2022 年 7 月 5 日的候选推荐草案以来的更改

自 2022 年 6 月 28 日的工作草案以来的更改

自 2022 年 4 月 28 日的工作草案以来的更改

自 2021 年 12 月 15 日的工作草案以来的更改

自 2021 年 6 月 1 日的工作草案以来的更改

自 2020 年 11 月 12 日的工作草案以来的更改

自 2019 年 11 月 5 日的工作草案以来的更改

自 2016 年 7 月 5 日的工作草案以来的更改

来自 Colors 3 的更改

与 CSS Color 3 相比,主要的变化是 CSS 颜色不再局限于 sRGB 的窄色域。

为了支持这一点,添加了几个全新的功能:

  1. 预定义的宽色域 RGB 色彩空间
  2. lab()lch()oklab()oklch() 函数,用于设备无关颜色

其他技术变更:

  1. <color> 的序列化现在在此处指定,而不是在 CSS 对象模型中指定
  2. hwb() 函数,用于以 HWB 表示法指定 sRGB 颜色。
  3. 添加了命名颜色 rebeccapurple

此外,还进行了一些语法更改:

  1. rgb()rgba() 函数现在接受 <number> 而不是 <integer>
  2. hsl()hsla() 函数现在接受 <angle><number> 作为色相。
  3. rgb()rgba(),以及 hsl()hsla() 现在是彼此的别名 (它们都有一个可选的 alpha)。
  4. rgb()rgba()hsl()hsla() 都获得了一种新的语法, 该语法由空格分隔的参数和一个可选的斜杠分隔的不透明度组成。 现在所有颜色函数都使用这种语法形式, 以符合 CSS 的函数表示法设计原则
  5. <alpha-value> 的所有用法现在都接受 <percentage><number>
  6. 添加了 4 位和 8 位十六进制颜色,以指定透明度。
  7. 添加了 none 值,用于表示无效分量。

20. 安全性考虑

系统颜色,如果它们实际上对应于用户的系统颜色,则会带来安全风险,因为它们使恶意软件网站更容易创建看起来像是来自系统的用户界面。然而,由于现在有几个系统颜色被定义为“通用”,因此认为此风险已得到缓解。

21. 隐私性考虑

本规范定义了“系统”颜色,理论上可能会暴露用户操作系统设置的详细信息,这是一种指纹识别风险。

22. 可访问性考虑

本规范鼓励作者不要仅使用颜色作为区分特征。

本规范鼓励浏览器确保特定系统颜色前景/背景对之间的足够对比度。曾考虑过使用具有特定 AA 或 AAA 对比度的更严格要求,但由于浏览器通常只是传递操作系统做出的颜色选择,或由用户选择(用户可能有特定要求,包括对偏头痛或癫痫发作患者降低对比度),CSSWG 无法要求特定的对比度水平。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合来表达。在本规范的规范性部分中,关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按照 RFC 2119 的描述进行解释。然而,为了可读性,这些词在本规范中并未全部以大写字母出现。

本规范的所有文本都是规范性的,除非明确标记为非规范性部分、示例和注释。[RFC2119]

本规范中的示例以“例如”开头,或者使用 class="example" 设置,使其与规范性文本分开,如下所示:

这是一个说明性示例。

说明性注释以“注”开头,并使用 class="note" 设置,与规范性文本分开,如下所示:

注,这是一个说明性注释。

建议性内容是引起特别注意的规范部分,并使用 <strong class="advisement"> 设置,与其他规范性文本分开,如下所示:用户代理必须提供可访问的替代方案。

测试

与本规范内容相关的测试可能会记录在像这样的“测试”块中。任何这样的块都是非规范性的。


一致性类别

本规范的一致性定义适用于三个一致性类别:

样式表
CSS 样式表
渲染器
用户代理(UA),用于解释样式表的语义并渲染使用它们的文档。
创作工具
用户代理(UA),用于编写样式表。

如果样式表中使用的所有语句都符合此模块中定义的通用 CSS 语法以及每个功能的个体语法,则该样式表符合本规范。

如果渲染器除了按照适当的规范解释样式表外,还支持本规范中定义的所有功能,正确地解析它们并相应地渲染文档,则渲染器符合本规范。然而,由于设备限制,UA 无法正确渲染文档的情况并不意味着 UA 不符合规范。(例如,UA 不要求在单色监视器上渲染颜色。)

如果创作工具编写的样式表在语法上符合通用 CSS 语法以及本模块中每个功能的个体语法,并且满足本模块中描述的样式表的一致性要求,则该创作工具符合本规范。

部分实现

为了使作者能够利用向前兼容的解析规则分配后备值,CSS 渲染器必须将任何无法使用的规则、属性、属性值、关键字和其他语法结构视为无效(并根据需要忽略)。特别是,用户代理不得选择性地忽略不支持的组件值并在单个多值属性声明中保留支持的值:如果任何值被视为无效(因为不支持的值必须是),CSS 要求忽略整个声明。

不稳定和专有功能的实现

为了避免与将来稳定的 CSS 功能发生冲突,CSSWG 建议遵循最佳实践,以实现不稳定功能和 CSS 的专有扩展

非实验性实现

一旦规范达到候选推荐阶段,就可以进行非实验性实现,实现者应发布任何 CR 级别功能的无前缀实现,前提是他们可以证明该实现符合规范并正确实现。

为了建立和维护 CSS 在各实现之间的互操作性,CSS 工作组要求非实验性 CSS 渲染器在发布任何 CSS 功能的无前缀实现之前,向 W3C 提交实现报告(如果需要,还需提交用于该实现报告的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组进行审核和修正。

有关提交测试用例和实现报告的更多信息,可以在 CSS 工作组的网站上找到:https://www.w3.org/Style/CSS/Test/。问题应通过public-css-testsuite@w3.org邮件列表进行咨询。

候选推荐退出标准

要将本规范推进到提议推荐阶段,每个功能必须至少有两个独立的、可互操作的实现。每个功能可以由不同的产品集实现,不要求所有功能由单个产品实现。对于此标准的目的,我们定义以下术语:

独立
每个实现必须由不同的团队开发,并且不能共享、重用或派生自另一合格实现所使用的代码。与本规范的实现无关的代码部分可不受此要求限制。
互操作性
通过官方 CSS 测试套件中的相应测试用例,或者,如果实现不是 Web 浏览器,则通过等效的测试。为了声称互操作性,测试套件中的每个相关测试都应创建一个等效的测试。此外,如果要使用这样的用户代理(UA)来声称互操作性,那么必须有一个或多个其他 UA 也能够以相同的方式通过这些等效测试,以实现互操作性。等效测试必须公开提供以便于同行审查。
实现
一个用户代理,它:
  1. 实现了规范。
  2. 对公众开放。实现可以是发布的产品或其他公开可用的版本(例如,测试版本、预览发布或“每夜构建”)。未发布的产品版本必须至少实现该功能一个月,以证明其稳定性。
  3. 不是实验性的(即,版本专门用于通过测试套件,且不打算用于今后的正常使用)。

本规范将保持候选推荐状态至少六个月。

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性引用

[Bradford-CAT]
Ming R. Luo; R. W. G. Hunt. 一种色适应转换和色彩不恒常指数。《色彩研究与应用》23(3) 154-158。1998 年 6 月。
[CIELAB]
ISO/CIE 11664-4:2019(E): 色度学 — 第 4 部分:CIE 1976 L*a*b* 色彩空间。2019 年。已发布。 网址:http://cie.co.at/publications/colorimetry-part-4-cie-1976-lab-colour-space-1
[COLORIMETRY]
色度学,第四版。CIE 015:2018。2018 年。网址:http://www.cie.co.at/publications/colorimetry-4th-edition
[Compositing]
Chris Harrelson。混合与合成级别 1。2024 年 3 月 21 日。候选推荐草案。网址:https://www.w3.org/TR/compositing-1/
[CSS-BACKGROUNDS-3]
Elika Etemad;Brad Kemper。CSS 背景与边框模块级别 3。2024 年 3 月 11 日。候选推荐草案。网址:https://www.w3.org/TR/css-backgrounds-3/
[CSS-CASCADE-5]
Elika Etemad;Miriam Suzanne;Tab Atkins Jr.。CSS 层叠与继承级别 5。2022 年 1 月 13 日。候选推荐。网址:https://www.w3.org/TR/css-cascade-5/
[CSS-COLOR-ADJUST-1]
Elika Etemad;等人。CSS 颜色调整模块级别 1。2022 年 6 月 14 日。候选推荐草案。网址:https://www.w3.org/TR/css-color-adjust-1/
[CSS-CONDITIONAL-3]
Chris Lilley;David Baron;Elika Etemad。CSS 条件规则模块级别 3。2024 年 8 月 15 日。候选推荐草案。网址:https://www.w3.org/TR/css-conditional-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.;Simon Sapin。CSS 语法模块级别 3。2021 年 12 月 24 日。候选推荐草案。网址:https://www.w3.org/TR/css-syntax-3/
[CSS-TEXT-DECOR-4]
Elika Etemad;Koji Ishii。CSS 文本装饰模块级别 4。2022 年 5 月 4 日。工作草案。网址:https://www.w3.org/TR/css-text-decor-4/
[CSS-VALUES-3]
Tab Atkins Jr.;Elika Etemad。CSS 值与单位模块级别 3。2024 年 3 月 22 日。候选推荐草案。网址:https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.;Elika Etemad。CSS 值与单位模块级别 4。2024 年 3 月 12 日。工作草案。网址:https://www.w3.org/TR/css-values-4/
[CSS2]
Bert Bos;等人。层叠样式表级别 2 修订版 1 (CSS 2.1) 规范。2011 年 6 月 7 日。推荐。网址:https://www.w3.org/TR/CSS2/
[CSSOM-1]
Daniel Glazman;Emilio Cobos Álvarez。CSS 对象模型 (CSSOM)。2021 年 8 月 26 日。工作草案。网址:https://www.w3.org/TR/cssom-1/
[Display-P3]
Apple 公司。Display P3。 2022 年 2 月。网址:https://www.color.org/chardata/rgb/DisplayP3.xalter
[DOM]
Anne van Kesteren。DOM 标准。现行标准。 网址:https://dom.spec.whatwg.org/
[HSL]
George H. Joblove, Donald Greenberg。计算机图形学的色彩空间。1978 年 8 月。网址:https://doi.org/10.1145/965139.807362
[HTML]
Anne van Kesteren;等人。HTML 标准。 现行标准。网址:https://html.spec.whatwg.org/multipage/
[HWB]
Alvy Ray Smith。HWB — 一种更直观的基于色相的颜色模型。1996 年。网址:http://alvyray.com/Papers/CG/HWB_JGTv208.pdf
[ICC]
ICC.1:2022 (配置文件版本 4.4.0.0)。2022 年 5 月。网址:http://www.color.org/specification/ICC.1-2022-05.pdf
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。现行标准。网址:https://infra.spec.whatwg.org/
[ITU-R-BT.601]
建议 ITU-R BT.601。网址:https://www.itu.int/rec/R-REC-BT.601/en
[ITU-R-BT.709]
建议 ITU-R BT.709。网址:https://www.itu.int/rec/R-REC-BT.709/en
[MEDIAQUERIES-5]
Dean Jackson;等人。媒体查询级别 5。 2021 年 12 月 18 日。工作草案。网址:https://www.w3.org/TR/mediaqueries-5/
[Oklab]
Björn Ottosson。一种用于图像处理的感知色彩空间。2020 年 12 月。网址:https://bottosson.github.io/posts/oklab/
[Rec.2020]
建议 ITU-R BT.2020-2:用于制作和国际节目交换的超高清电视系统的参数值。2015 年 10 月。网址:http://www.itu.int/rec/R-REC-BT.2020/en
[RFC2119]
S. Bradner。RFC 中用于表示要求级别的关键词。1997 年 3 月。最佳当前实践。网址:https://datatracker.ietf.org/doc/html/rfc2119
[ROMM]
ISO 22028-2:2013 摄影和图形技术 — 用于数字图像存储、处理和交换的扩展颜色编码 — 第 2 部分:参考输出介质度量 RGB 彩色图像编码 (ROMM RGB)。2013 年 4 月。网址:https://www.iso.org/standard/56591.html
[SMPTE296]
ST 296:2012, 1280 × 720 逐行扫描图像 4:2:2 和 4:4:4 采样结构 — 模拟和数字表示及模拟接口。2012 年 5 月 17 日。标准。网址:https://doi.org/10.5594/SMPTE.ST296.2012
[SRGB]
多媒体系统和设备 - 色彩测量和管理 - 第 2-1 部分:色彩管理 - 默认 RGB 色彩空间 - sRGB。网址:https://webstore.iec.ch/publication/6169
[SVG11]
Erik Dahlström;等人。可缩放矢量图形 (SVG) 1.1 (第二版)。2011 年 8 月 16 日。推荐。网址:https://www.w3.org/TR/SVG11/

信息性引用

[CSS-ANIMATIONS-1]
David Baron;等。CSS 动画级别 1。2023 年 3 月 2 日。工作草案。网址:https://www.w3.org/TR/css-animations-1/
[CSS-COLOR-5]
Chris Lilley;等。CSS 颜色模块级别 5。 2025 年 3 月 18 日。工作草案。网址:https://www.w3.org/TR/css-color-5/
[CSS-IMAGES-4]
Tab Atkins Jr.;Elika Etemad;Lea Verou。CSS 图像模块级别 4。2023 年 2 月 17 日。工作草案。网址:https://www.w3.org/TR/css-images-4/
[CSS-TRANSITIONS-1]
David Baron;等。CSS 过渡。2018 年 10 月 11 日。工作草案。网址:https://www.w3.org/TR/css-transitions-1/
[CSS3-TEXT-DECOR]
Elika Etemad;Koji Ishii。CSS 文本装饰模块级别 3。2022 年 5 月 5 日。候选推荐草案。网址:https://www.w3.org/TR/css-text-decor-3/
[DCI-P3]
SMPTE 推荐实践 - D-Cinema 质量 — 参考投影仪和环境。2011 年。网址:https://pub.smpte.org/latest/rp431-2/rp0431-2-2011.pdf
[FILTER-EFFECTS-1]
Dirk Schulze;Dean Jackson。滤镜效果模块级别 1。2018 年 12 月 18 日。工作草案。网址:https://www.w3.org/TR/filter-effects-1/
[JPEG]
Eric Hamilton。JPEG 文件交换格式。1992 年 9 月。网址:https://www.w3.org/Graphics/JPEG/jfif3.pdf
[PNG]
Chris Lilley;等。便携式网络图形 (PNG) 规范(第三版)。2025 年 3 月 13 日。候选推荐。网址:https://www.w3.org/TR/png-3/
[ROMM-RGB]
ROMM RGB。网址:https://www.color.org/chardata/rgb/rommrgb.xalter
[Sharma]
G. Sharma;W. Wu;E. N. Dalal。CIEDE2000 色差公式:实现说明、补充测试数据和数学观察。 2005 年 2 月。网址:https://www.hajim.rochester.edu/ece/sites/gsharma/ciede2000/
[TIFF]
TIFF 修订版 6.0。1992 年 6 月 3 日。网址:https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml
[Understanding_CCT]
什么是 CCT?选择照明相关色温指南。2024 年 8 月 14 日。网址:https://litomatic.com/blog/what-is-cct-in-lighting/
[WCAG21]
Michael Cooper;等。Web 内容可访问性指南 (WCAG) 2.1。2024 年 12 月 12 日。推荐。网址:https://www.w3.org/TR/WCAG21/
[Wolfe]
Geoff Wolfe。ProPhoto RGB 颜色编码的设计与优化。2011 年 12 月 21 日。网址:http://www.realtimerendering.com/blog/2011-color-and-imaging-conference-part-vi-special-session/

属性索引

名称 初始值 适用于 继承性 百分比 动画类型 规范顺序 计算值
color <color> CanvasText 所有元素及文本 N/A 按计算值类型 按语法 计算颜色,见解析颜色值
opacity <opacity-value> 1 所有元素 映射到范围 [0,1] 按计算值类型 按语法 指定数字,限制在范围 [0,1]