CSS 颜色模块 第4级

W3C 候选推荐草案,

关于本文件的更多详情
此版本:
https://www.w3.org/TR/2026/CRD-css-color-4-20260227/
最新发布版本:
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. (Google)
Chris Lilley (W3C)
Lea Verou (Invited Expert)
Former Editor:
L. David Baron (Google)
前任编辑:
L. David Baron (Mozilla)
为此规范建议编辑:
GitHub 编辑器
测试套件:
https://wpt.fyi/results/css/css-color/

摘要

本规范描述了 CSS <color> 取值,以及用于前景色和分组不透明度的属性。 还介绍了颜色的插值方法和色域映射。

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

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新修订版可在 W3C 标准与草案索引 中找到。

本文档由 CSS 工作组 按照 推荐标准流程候选推荐草案形式发布。 发布为候选推荐并不代表 W3C 及其成员的认可。 候选推荐草案整合了工作组打算在后续候选推荐快照中加入的来自上一个候选推荐的变更。

本文档为草案版本,可能随时更新、替换或被其他文档作废。 不应将本文档作为除进行中的工作外的正式引用。

请通过在 GitHub 提交问题(优先),反馈内容时请在标题中包含规范代码 “css-color”,格式如下: “[css-color] …评论摘要…”。 所有问题与评论都已归档。 另外,也可将反馈发送至(已归档)公共邮件列表 www-style@w3.org

本文档受 2025年8月18日 W3C 流程文件 管辖。

本文档由按照 W3C 专利政策 运作的小组制作。 W3C 维护了 该工作组任何专利披露的公开列表; 该页面也包含专利披露的说明。 如果个人掌握某项专利且认为其中包含必要声明, 他/她必须依据 W3C 专利政策第六节进行披露。

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 章)。

opacity 属性的值 不会影响命中测试。

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预计可以阅读,尽管对比度可能较低,在任何背景上都可以阅读。

为保持与组件强调色样式的一致性, AccentColor 的取值来自 accent-color, 除非启用了强制色彩模式AccentColorText 的取值为 和 AccentColor 对比的前景色, 具体如组件 强调色样式中所描述。

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

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 方便生成。 接着所有展示属性都从用连字符的写法 改成了驼峰式,因为 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,它们都是本规范此级新增内容, 所以 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(85.9017% 166.116 138.207);

在 display-p3 中,该颜色为

color(display-p3 -0.350289 1.00707 -0.144209);

且超出了 display-p3 的色域 (红色和蓝色为负值,绿色大于1)。 如果你有显示 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. 预定义线性光 Display P3 色彩空间:display-p3-linear 关键字

display-p3-linear 预定义色彩空间 与 display-p3 唯一的区别是其变换函数 是线性光(没有伽玛编码)。

其具有以下特性:

x y
红色色度 0.680 0.320
绿色色度 0.265 0.690
蓝色色度 0.150 0.060
白色色度 D65
变换函数 恒等函数,见下方
白色亮度 80.0 cd/m2
黑色亮度 0.80 cd/m2
图像状态 显示参考
百分比 R、G 和 B 都允许
百分比参考范围 对于 R、G、B:0% = 0.0,100% = 1.0
cl = c;

c 为红色、绿色或蓝色分量, cl 为相应的线性光分量,且与 c 完全一致。

为避免出现色带伪影, display-p3-linear 要求的精度更高 相较于 display-p3

例如,以下是同一个颜色
 color(display-p3 0.591 0.123 0.264)
 color(display-p3-linear 0.3081 0.014 0.0567)
测试

10.6. 预定义 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
diagram of a98 primaries and secondaries in LCH
A98 色彩空间在 LCH 中的可视化。 图中展示了主色和二次色 (但呈现为 sRGB 色,不是实际色彩)。 对比参考,还显示了 sRGB 主色和二次色(虚线圆)。 a98 主色具有更高色度, 尤其是黄色、绿色和青色。
测试

10.7. 预定义 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.8. 预定义的 ITU-R BT.2020-2 颜色空间:rec2020 关键字

rec2020
rec2020 [Rec.2020] 色彩空间接受三个数值参数, 分别代表颜色的红、绿、蓝分量。 色域内的颜色这三个分量都在 [0, 1] 范围内, (在视频术语中称为“满范围”)。 ITU 参考 2020 用于 超高清、4k 和 8k 电视。

主色可以物理实现, 但难度很高, 因为它们非常接近光谱曲线边界。

目前的显示器无法还原 rec2020 的全部色域。 随着显示技术提升,覆盖率未来会逐步增加。

其具有以下特性:

x y
红色色度 0.708 0.292
绿色色度 0.170 0.797
蓝色色度 0.131 0.046
白色色度 D65
变换函数 伽玛 2.40,参考 [REC_BT.1886]
图像状态 显示参考
百分比 R、G 和 B 都允许
百分比参考范围 R、G、B:0% = 0.0,100% = 1.0
diagram of rec2020 primaries and secondaries in LCH
rec2020 色彩空间在 LCH 中的可视化。图中展示了主色和二次色 (但显示为 sRGB 色,而非真实色彩)。 作为对比,还显示了 sRGB 主色和二次色(虚线圆)。 rec2020 主色具有更高的色度。
测试

10.9. 预定义的 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.10. 将预定义的颜色空间转换为 Lab 或 Oklab

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

  1. 将伽玛编码的 RGB 转换为线性光 RGB(撤销伽玛编码)
  2. 将线性 RGB 转换为 CIE XYZ
  3. 如需,将 D65 白点 (用于 sRGBdisplay-p3a98-rgb 以及 rec2020) 转换为 Lab 所用的 D50 白点, 使用线性 Bradford 变换。prophoto-rgb 已采用 D50 白点。
  4. 将 D50 适配的 XYZ 转换为 Lab

转换为 Oklab 类似, 但色度适配步骤 只需要对 prophoto-rgb 进行。

  1. 将伽玛编码的 RGB 转换为线性光 RGB(撤销伽玛编码)
  2. 将线性 RGB 转换为 CIE XYZ
  3. 如需,将 D50 白点(用于 prophoto-rgb) 转换为 Oklab 所用的 D65 白点, 使用线性 Bradford 变换。
  4. 将 D65 适配的 XYZ 转换为 Oklab

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

10.11. 将 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.12. 在预定义 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.13. 简单的 Alpha 合成

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

11. 转换颜色

测试

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


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

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

  1. 如果 src圆柱极坐标色彩表示, 首先将 col1 转换为对应的矩形正交色彩表示, 并将其作为新的 col1
  2. 将所有缺省分量替换为零。
  3. 如果 src 不是 线性光表示法, 则将其转换为线性光(撤销伽玛编码), 并将其作为新的 col1
  4. col1 按指定白点 src-white 转换为 CIE XYZ, 并将其命名为 xyz
  5. 如果 dest-whitesrc-white 不同, 用线性 Bradford 色度适配变换xyz 进行色度适配到 dest-white, 并将其作为新的 xyz
  6. 如果 dest圆柱极坐标色彩表示, 则令 dest-rect 为对应的矩形正交色彩表示。 否则,令 dest-rectdest
  7. xyz 转换为 dest, 并执行任何变换函数(伽玛编码), 生成 col2
  8. 如果 dest 是物理输出色彩空间,如显示设备, 则必须对 col2 进行css 色域映射, 以确保它可显示
  9. 如果 dest-rectdest 不同, 即 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 | display-p3-linear | a98-rgb | prophoto-rgb | rec2020 | lab | oklab | <xyz-space>
<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 色彩格式之间的插值 (十六进制颜色、命名颜色、 rgb()hsl()hwb() 以及包括 alpha 的等价形式) 在伽玛编码的 sRGB 空间进行。 这样可确保 Web 兼容性; 旧版 sRGB 内容默认在 sRGB 空间内插值。

测试

这也意味着作者可以选择 启用更优的插值效果, 即使是针对 sRGB 颜色, 只需对至少一个颜色使用非旧版 color(srgb r g b) 形式, 或明确指定 插值色彩空间

测试

如果待插值的颜色超出了 插值色彩空间 的色域, 那么转换到该空间后, 结果会包含超出范围的值。

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

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)

如果继承的 缺失分量 是透明度,则必须用继承值对颜色做预乘, 而不是使用颜色转换所得的零值。

例如,如果下列两种颜色插值, 第二种颜色有透明度缺失:
 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. 色域映射简介

注意: 本节为文档其他部分 所述具体要求提供重要背景。

本节不具规范性

测试

本节不是规范要求,无需测试。


当一个原色空间的颜色 被转换到另一个色域更小的目标色空间时, 某些颜色值会超出目标色域。

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

色域映射就是在视觉效果尽量不显著变化的前提下, 找到一个色域内的颜色的过程。

有些超出色域的颜色对应真实世界的颜色 (理论上可物理呈现), 另一些则是虚拟色彩 (它们超出了光谱包络线, 需要超过 100% 的单一波长能量) 永远无法物理实现。 这种颜色通常由计算产生, 例如“令此颜色饱和度提高 100 倍”。

例如,虽然理论上无限, CIE Lab (a,b) 平面实际实现上通常 把 ab 约束在 ±127 范围内。

这四个角中有三个超出了光谱包络线, 对应虚拟色彩。

±127 (a,b) 平面的四个角, 在 UCS 色度图上的位置。 外侧轮廓是光谱包络线。 大三角形是 rec2020 色域, 小三角是 sRGB 色域。

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°。

如果颜色并非严重超出色域, 剪裁还能带来可接受的效果。 尤其是对较暗或为负的 分量值。

例如, 设有颜色 color(rec2020 0.54 0.9 0)oklch(80.72% 0.3296 141.6)

转换到 p3 色彩空间时,负值的蓝色分量表明 该颜色已经超出色域范围: color(display-p3 0.3265 0.9165 -0.1262) 在线性光下为 color(display-p3-linear 0.0871 0.8205 -0.0146)

对 gamma 编码的 p3 颜色做 gamut 剪裁得到 color(display-p3 0.3265 0.9165 0) 在线性光下为 color(display-p3-linear 0.0871 0.8205 0) 对比得 oklch(80.79% 0.3221 142.3)

这是个不错的结果; 色相角和明度几乎没变, 但色度有所降低,属于预期范围。

就线性光红绿蓝百分比而言, 红绿完全一样, 而蓝色高出 -1.46%。

例如, 设有颜色 color(prophoto-rgb 0.2 1.0 0.1)oklch(85.07% 0.4873 151.4)

转换到 p3 色彩空间后,该颜色明显 超出色域: color(display-p3 -0.5782 1.067 -0.2363) 在线性光下为 color(display-p3-linear -0.2937 1.158 -0.0456)

对 gamma 编码的 p3 颜色做 gamut 剪裁得到 color(display-p3 0 1 0) 在线性光下依然为 color(display-p3-linear 0 1 0) (分量值恰好为0或1时不受 gamma 影响) 对比得 oklch(84.88% 0.3685 145.6)

视觉效果不如前例,但仍可接受。 色相影响略大,变化了 5.8°。

就线性光红绿蓝百分比而言, 红色高 57%,绿色低 6.7%,蓝色高 23%。

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 色域 (没有分量为负或大于一), 就得到了映射到色域内的颜色。

   color(oklch 0.96476 0.21094 110.23)

其对应

   color(srgb 0.99116 0.99733 0.00001)
OkLCh 色彩空间的一个常色相切面。 纵轴代表亮度, 横轴代表色度。 要映射的颜色, 用黄色圆标示, 会在保持色相和亮度不变的情况下降低色度。 因此颜色会沿图中的栗色线移动, 朝左侧的中性轴靠近。 sRGB 的色域边界用绿色显示。

13.1.4. 过度的色度减少

此外,这种简单的 MINDE 方法对于某些颜色会得到次优结果, 主要是非常浅亮的颜色, 如黄色和青色, 当色域边界的上沿非常浅或者略呈凹面时, 常亮射线可能刚好掠过色域边界, 导致色度异常低。

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

例如,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 色域,最后 映射出来的黄色色度也很好。 在 OK LCH 进行简单色域映射结果可接受。

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. 偏离感知均匀性:色相曲率

即使使用 deltaE2000 距离度量, 在 CIE LCH 色彩空间进行色域映射, 已知会产生次优结果, 对于色相在 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 色域映射算法 适用于单独的标准动态范围(SDR)CSS 颜色, 当其超出 RGB 显示器的色域时, 需要进行CSS 色域映射

实现可以根据性能和质量的权衡, 选择三种算法中的任意一种, 并且必须在 CSS 要求色域映射时使用选定算法。

它们都采用了相对比色渲染意图, 因此目标色域内的颜色不变。

注: 其它情况, 特别是映射到最大黑值显著高于零的打印机色域时, 需要用不同的算法, 使黑点和白点对齐, 这会导致极浅或极深颜色在降低色度时亮度发生变化。

注: 这些算法适用于单独的、独立的颜色; 对于色彩图像, 当相邻像素关系很重要,且需要保留细节和纹理时, 更适合使用感知渲染意图, 在那种情况下,色域内的颜色也可能会变化。

三种 CSS 色域映射算法的目标都是 在OkLCh 色彩空间中 恒亮度、恒色相地降低色度。

对于亮度超出范围的颜色, 若亮度大于等于 1.0,则目标色空间返回白色, 若亮度小于等于 0.0,则目标色空间返回黑色。

13.2.1. 二分查找色域映射(Local MINDE)

该二分查找算法 使用的颜色差公式为deltaEOK。 采用local-MINDE改进。 在每一步搜索中, 都计算当前映射颜色与其裁剪版本的 deltaEOK。

如果当前颜色色域边界外, 但与裁剪版本的 deltaEOK 低于“刚刚可觉察差异”(JND)阈值, 则返回裁剪颜色作为映射结果。 这样可以获得良好的效果且几乎没有色相漂移, 避免了在凹形色域表面附近过度色度降低, 但计算量可能较大。

对 OkLCh 色彩空间, 一个 JND 代表 0.02 的 OkLCh 差值。

注: 在 CIE Lab 色彩空间, 亮度范围 0 到 100, 并用 deltaE2000 时, 一个 JND 是 2。 因为 Oklab 和 OkLCh 的亮度是 0 到 1, 用 deltaEOK 时, 一个 JND 小 100 倍。

注: 为方便实验和对比, 二分查找(Local MINDE)算法已在 Coloraide(Python)[Coloraide-MINDE] 与 color.js(JavaScript)[colorjs-MINDE]实现。

13.2.2. 二分查找色域映射(Local MINDE)伪代码示例

要对一个颜色origin 在颜色空间origin color space中, 通过二分查找结合局部MINDE进行色域映射 使其进入目标颜色空间destination的色域:
  1. 如果destination没有色域限制(XYZ-D65、XYZ-D50、Lab、LCH、Oklab、OkLCh),直接将 origin转换为destination并返回,作为色域映射后的颜色
  2. origin_OkLChoriginorigin color space转换到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色域内。 对于HSL和HWB,如果该颜色在sRGB色域内则返回true。
  6. 如果 inGamut(origin_OkLCh) 为true,直接将origin_OkLCh 转换为destination并返回,作为色域映射后的颜色
  7. 否则,令 delta(one, two) 为一个函数,计算两颜色的deltaEOK差异
  8. JND 为0.02
  9. epsilon 为0.0001
  10. 令 clip(color) 为一个函数,将color转换为destination, 将每个分量钳制到其参考范围内,并返回结果
  11. currentorigin_OkLCh
  12. clipped 为 clip(current)
  13. E 为 delta(clipped, current)
  14. 如果 E < JND
    1. 返回 clipped 作为色域映射后的颜色
  15. min 为零
  16. maxorigin_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 作为色域映射后的颜色

13.2.3. EdgeSeeker色域映射

EdgeSeeker算法是一种基于几何与查找表的色域映射方法, 最初由Alexey Ardov为color.js库开发 [colorjs-EdgeSeeker]

对于任意给定的色相,色域边界切片 由上方的曲线段和下方的线性段组成, 在该色相的最大色度点处连接。

初始化本算法时, 针对目标RGB空间, 构建一个查找表(LUT), 包含每个色相切片上的最大色度OkLCh颜色。 对实际映射颜色的色相,使用查找表中最近值做线性插值, 估算其对应的最大色度值。

随后计算常亮射线与色域边界的交点, 对于边界下部(线性段)非常快, 上部(曲线段)效率也很高。

这种方法效果理想,但查找表(LUT)会占用更多内存。

13.2.4. EdgeSeeker色域映射伪代码示例

添加EdgeSeeker GMA伪代码

13.2.5. Ray Trace色域映射

Ray Trace算法是一种基于几何的RGB色域映射方法, 可实现快速常亮色度压缩。 它最初由Isaac Muse为Coloraide Python库开发 [Coloraide-Ray-Trace]

要进行映射的颜色首先会被转换为Oklch, 然后生成该颜色的无彩色对应版本,作为中性轴锚点。 这两个颜色接着会被转换为目标RGB空间的线性光形式。

由于此时色域边界为坐标轴对齐的立方体, 求交点运算更迅速。

从RGB立方体内部,沿锚点与当前颜色的连线发射射线, 交点即为第一次色域映射的近似值。

由于RGB空间的感知均匀性较差, 实际等色相、等亮度的射线在RGB空间为曲线轨迹。

第一次近似结果会被转换回Oklch, 并投影到色度压缩曲线上以修正色相与亮度, 得到新的当前颜色,更接近压缩色度的理想点。

此过程最多再迭代三次, 每次都能使结果逼近该曲线上的最佳位置。 最后一步通过简单的钳制以处理浮点误差。

该方法效果可与低JND的二分查找局部MINDE媲美, 但速度更快、耗时更为稳定。

Ray Trace色域映射到sRGB色域, 展示色度压缩曲线的迭代逼近过程, 最多进行四次迭代。
图片版权归Isaac Muse所有。

注:为便于实验和实现对比, Ray Trace算法可在Coloraide库(Python实现) [Coloraide-Ray-Trace] 及color.js库(JavaScript实现)[colorjs-RayTrace]中获取。

13.2.6. Ray Trace色域映射伪代码示例

对一个颜色origin 在颜色空间origin color space中, 通过Ray Trace色域映射 使其进入RGB目标色域destination
  1. 如果destination没有色域限制(XYZ-D65、XYZ-D50、Lab、LCH、Oklab、OkLCh),则直接将 origin转换为destination并返回作为色域映射后的颜色
  2. origin_OkLChoriginorigin color space转换为OkLCh颜色空间的结果
  3. origin_OkLCh的亮度大于等于100%, 将`oklab(1 0 0 / origin.alpha)`转换为destination并返回
  4. origin_OkLCh的亮度小于等于0%, 将`oklab(0 0 0 / origin.alpha)`转换为destination并返回
  5. l_originorigin_OkLCh的OkLCh亮度分量
  6. h_originorigin_OkLCh的OkLCh色相分量
  7. anchor为一个无色度OkLCh颜色,取 l_origin作为亮度,0为色度,h_origin为色相, 并转为线性光destination
  8. origin_rgborigin_OkLCh 转为线性光destination
  9. origin_rgb不在色域内
    • low为 0.0 + 1E-6 1
    • high为 1.0 - 1E-6 2
    • lastorigin_rgb
    • 循环 (i=0; i<4; i++)
      • 如果 (i > 0)
        • current_OkLChorigin_rgb转为OkLCh
        • current_OkLCh亮度为l_origin
        • current_OkLCh色相为h_origin 3
        • origin_rgbcurrent_OkLCh 转为线性光destination
      • 投射射线,起点为anchor,终点为origin_rgb, 得到射线与色域边界的交点intersection
      • 如未找到交点, 令origin_rgblast并退出循环 5
      • 若 (i >0) 且 (origin_rgb每个分量都在lowhigh之间), 则令anchororigin_rgb 4
      • origin_rgbintersection
      • lastintersection
  10. 令clip(color)为一个函数,将color转为destination, 再将每分量钳制到参考范围,并返回
  11. clipped为clip(current)
  12. 返回clipped作为色域映射后的颜色
在一个线性光RGB空间内 从startend (用于色域映射时, start为RGB色域内的锚点, end为位于立方色域表面的色域映射结果):
  1. bminbmax为 分别指定色域下界与上界的三个分量数组 6
  2. tfar为无穷(或非常大的数)
  3. tnear为负无穷(或非常小的负数)
  4. direction为三个分量的数组
  5. 循环 (i = 0; i < 3; i++):
    • astart [i]
    • bend [i]
    • db-a
    • direction [i]d
    • 如果 abs(d) < 1E-12
      • inv_d为1/d
      • t1 = (bmin [i] - a) * inv_d
      • t2 = (bmax [i] - a) * inv_d
      • tnear为max(min(t1, t2), tnear)
      • tfar为min(max(t1, t2), tfar)
    • 否则,如果(a < bmin[i]a > bmax[i])
      • 返回未找到交点
  6. 如果 (tnear > tfartfar < 0)
    • 返回未找到交点
  7. 如果 tnear < 0
    • tneartfar 7
  8. 如果 tnear为无穷大(或等于初始大数)
    • 返回未找到交点
  9. 循环 (i =0; i < 3; i++):
    • result [i]start [i] + direction [i] * tnear
  10. 返回result
13.2.6.1. Ray Trace算法脚注
  1. 这里假设最小值为0, 且所有通道的最小值一致。 该值应相对于单位较小。 64位情况下可小至1e-14,实际用1e-6已足够。
  2. 1.0代表通道的最大有效值, 且假定各通道的最大值相同。
  3. 这一步让current颜色重新回到色度压缩曲线, 以防其偏离。
  4. 这意味着origin_rgb低于色域表面, 因此用它作为锚点,更接近色域表面。
  5. 这用于极端情况下, 映射空间彻底崩溃时的兜底, 比如超出可见光谱的极端颜色。 CSS中的非虚构色通常不会触发这类兜底。
  6. 对于典型RGB空间, 色域下界为0,上界为1, 因此可以简化为常量, 而无需3分量数组。
  7. 优先选取startend方向上的第一个交点。

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) 因为 calc() 在 HSL 到 RGB 转换时已丢失。

同样出于历史原因, 当 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)

虽然 a、b 和 C 的数值 理论上没有上限, 但对于趋于无穷大的数值, 实现上可能会有 实现自定义的极限

测试

14.3. 解析 Oklab 和 OkLCh 数值

此项适用于 oklab()oklch() 数值。

声明值、计算值和使用值 为对应的 Oklab 或 OkLCh 颜色 (L、C、H 分量已经裁剪后) 以及指定的 alpha 分量 (为<number> ——不是<percentage>; 未指定时 alpha 默认为不透明)。

例如,下列计算值:

 oklch(42.1% 0.192 328.6 / 1)

 oklch(42.1% 0.192 328.6)

虽然 a、b 和 C 的取值理论上无限制, 但对于趋近无穷大的数值, 实现上可能有实现自定义的极限

测试

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)

虽然 r、g、b、x、y 和 z 的数值 理论上没有上限, 但对于趋近无穷大的数值, 实现上可能会有 实现自定义的极限

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 分量的保留精度, 以及序列化值中的有效数字位数, 本规范未明确定义, 但至少要保证 能够不失真地还原八位数值(round-trip)。 数值必须向 +∞ 舍入, 不允许直接截断。

注: 脚本作者若期望 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
display-p3-linear 12
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 属性。

如果指定的不透明度值 与字面 <percentage-token> 匹配(即未使用 calc()), 则应将其序列化为等价的 <number>(0% 对应 0,100% 对应 1)值。 否则,不透明度的指定值 应按语法的标准序列化方式序列化。

这种 <number> 值用十进制表示, 小数点用 “.” 。 不可省略前导零。 必须省略末尾零。

超出 [0,1] 区间的不透明度值 保留原样,不进行截断,在序列化的指定值中体现。

不透明度值的保留精度, 以及序列化时的小数位数, 本规范未明确定义, 但至少要足以 完整还原为整数百分比值。 因此,序列化值必须至少包含 两位小数(除非末尾零已去除)。 数值必须 向 +∞ 舍入,不能直接截断。

17. 默认样式规则

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

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

18. 颜色转换的示例代码

本节不是规范性的。

测试

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


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

    // 颜色转换示例代码
    // 转换也可以使用 ICC 配置文件和色彩管理系统完成
    // 为简明起见,矩阵乘法调用了库(multiply-matrices.js)
    
    // 标准白点,用四位 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) {
    	// 将 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, 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
    	// 比 Adobe RGB 1998 文档第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) {
    	// 将 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) {
    	// 将 rec2020 RGB 数组(范围 0.0 - 1.0)转换为线性光形式
    	// 参考电光传递函数来自 Rec. ITU-R BT.1886 附录1
    	// b(黑电平提升)= 0,a(用户增益)= 1
    	// 在扩展范围上定义,不进行裁剪
    
    	return RGB.map(function (val) {
    		let sign = val < 0? -1 : 1;
    		let abs = Math.abs(val);>
    		return sign * Math.pow(abs, 2.4);
    	});
    }
    
    function gam_2020(RGB) {
    	// 将线性光 rec2020 RGB 数组(范围 0.0-1.0)转换为伽马校正形式
    	// 参考电光传递函数来自 Rec. ITU-R BT.1886 附录1
    	// b(黑电平提升)= 0,a(用户增益)= 1
    	// 在扩展范围上定义,不进行裁剪
    
    	return RGB.map(function (val) {
    		let sign = val < 0? -1 : 1;
    		let abs = Math.abs(val);>
    		return sign * Math.pow(abs, 1 / 2.4);
    	});
    }
    
    function lin_2020_to_XYZ(rgb) {
    	// 将线性光 rec2020 值转换为 CIE XYZ
    	// 使用 D65(不做色度适应)
    	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) {
    	// 将 XYZ 转换为线性光 rec2020
    	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) {
    	// 使用 Bradford 方法从 D65 适应到 D50
    	// 下列矩阵是三步操作的结果:
    	// - 从 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) {
    	// 使用 Bradford 方法从 D50 适应到 D65
    	// 参见 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 时乘以 100 并加上百分号
    }
    
    function Lab_to_XYZ(Lab) {
    	// 将 Lab 转换为 D50 适配的 XYZ
    	// http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.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)); // 色度
    	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 保持不变
    		chroma, // 色度
    		hue // 色相,度数范围 [0 到 360)
    	];
    }
    
    function LCH_to_Lab(LCH) {
    	// 从极坐标形式转换
    	return [
    		LCH[0], // 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 保持不变
    		chroma,
    		hue
    	];
    }
    
    function OKLCH_to_OKLab(OKLCH) {
    	return [
    		OKLCH[0], // L 保持不变
    		OKLCH[1] * Math.cos(OKLCH[2] * Math.PI / 180), // a
    		OKLCH[1] * Math.sin(OKLCH[2] * Math.PI / 180)  // b
    	];
    }
    
    // 预乘 alpha 的转换
    
    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} 测量 sample 相对于 reference 的色差
       */
      
      function deltaE2000 (reference, sample) {
      
          // 给定 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 轴
      	// 顺带一提,这也是为什么没有 Lab2000 色彩空间
      	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);
      
      	// 补偿 Lab 空间蓝色区域的非线性
      	// 色相加权因子有四种可能,
      	// 取决于角度,以获得正确符号
      	let hdash;
      	if (Cdash1 * Cdash2 === 0) {
      		hdash = hsum;
      	}
      	else if (habs <= 180) {
      		hdash = hsum / 2;
      	}
      	else if (hsum < 360) {
      		hdash = (hsum + 360) / 2;
      	}
      	else {
      		hdash = (hsum - 360) / 2;
      	}
      
      	// 对 CIELAB 非均匀性的方位修正
      	// 这些都试图让 JND 椭球更像球
      
      	// SL 明度加权因子
      	// 假设背景 L=50
      	let lsq = (Ldash - 50) ** 2;
      	let SL = 1 + ((0.015 * lsq) / Math.sqrt(20 + lsq));
      
      	// SC 色度因子,类似于 CMC 和 deltaE 94 的公式
      	let SC = 1 + 0.045 * Cdash;
      
      	// 蓝色非线性校正的交叉项 T
      	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));
      
      	// SH 色相因子依赖于色度,
      	// 以及经过调整的色相角,类似于 deltaE94。
      	let SH = 1 + 0.015 * Cdash * T;
      
      	// RT 色相旋转项补偿了 JND 椭圆和
      	// Munsell 等色线在中高色度蓝色区域的旋转
      	//(色相 225 到 315)
      	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;
      
      	// 最后逐项取平方和开方得到 deltaE
      	let dE = (ΔL / SL) ** 2;
      	dE += (ΔC / SC) ** 2;
      	dE += (ΔH / SH) ** 2;
      	dE += RT * (ΔC / SC) * (ΔH / SH);
      	return Math.sqrt(dE);
      	// 成功!!!
      };

      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。

        变更记录

        2025年4月24日 候选推荐草案 以来的变更

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

        信息性引用

        [Coloraide-MINDE]
        Isaac Muse。OkLCh 色度。2024年。URL: https://facelessuser.github.io/coloraide/gamut/#oklch-chroma
        [Coloraide-Ray-Trace]
        Isaac Muse。射线追踪色度降低。2024年。URL: https://facelessuser.github.io/coloraide/gamut/#ray-tracing-chroma-reduction
        [colorjs-EdgeSeeker]
        Alexey Ardov。EdgeSeeker 色域映射算法。2023年。URL: https://github.com/color-js/apps/tree/main/gamut-mapping/edge-seeker
        [colorjs-MINDE]
        Chris Lilley。色域映射。2022年。URL: https://colorjs.io/docs/gamut-mapping
        [colorjs-RayTrace]
        Isaac Muse。射线追踪色域映射。2023年。URL: https://github.com/color-js/apps/blob/2c7346dd00855f7b82eb4c7527355a09b84beeb3/gamut-mapping/methods.js#L273
        [CSS-ANIMATIONS-1]
        David Baron 等。CSS 动画第1级。2023年3月2日。WD。URL: https://www.w3.org/TR/css-animations-1/
        [CSS-COLOR-5]
        Chris Lilley;Una Kravets;Lea Verou。CSS 颜色模块第5级。2026年2月27日。WD。URL: https://www.w3.org/TR/css-color-5/
        [CSS-IMAGES-4]
        Elika Etemad;Tab Atkins Jr.;Lea Verou。CSS 图像模块第4级。2025年9月30日。WD。URL: https://www.w3.org/TR/css-images-4/
        [CSS-TRANSITIONS-1]
        Chris Marrin 等。CSS 过渡模块第1级。2026年1月8日。WD。URL: https://www.w3.org/TR/css-transitions-1/
        [CSS3-TEXT-DECOR]
        Elika Etemad;Koji Ishii。CSS 文本装饰模块第3级。2022年5月5日。CRD。URL: https://www.w3.org/TR/css-text-decor-3/
        [DCI-P3]
        SMPTE 推荐规范 - 数字影院质量 —— 参考投影仪与环境。2011年。URL: http://ieeexplore.ieee.org/document/7290729/
        [FILTER-EFFECTS-1]
        Dirk Schulze;Dean Jackson。滤镜效果模块第1级。2018年12月18日。WD。URL: https://www.w3.org/TR/filter-effects-1/
        [JPEG]
        Eric Hamilton。JPEG 文件交换格式。1992年9月。URL: https://www.w3.org/Graphics/JPEG/jfif3.pdf
        [PNG]
        Chris Lilley 等。可移植网络图形 (PNG) 规范(第三版)。2025年6月24日。REC。URL: https://www.w3.org/TR/png-3/
        [REC_BT.1886]
        ITU-R BT.1886 用于高清电视工作室制作的平板显示器参考电光转换函数。2011-03。URL: https://www.itu.int/rec/R-REC-BT.1886/en
        [ROMM-RGB]
        ROMM RGB。URL: https://www.color.org/chardata/rgb/rommrgb.xalter
        [Sharma]
        G. Sharma;W. Wu;E. N. Dalal。CIEDE2000 色差公式:实现笔记、补充测试数据与数学说明。2005年2月。URL: https://www.hajim.rochester.edu/ece/sites/gsharma/ciede2000/
        [TIFF]
        TIFF 6.0 版。1992年6月3日。URL: https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml
        [Understanding_CCT]
        何为CCT?照明相关色温选购指南。2024-08-14。URL: https://litomatic.com/blog/what-is-cct-in-lighting/
        [WCAG21]
        Michael Cooper 等。网页内容可访问性指南 (WCAG) 2.1。2025年5月6日。REC。URL: https://www.w3.org/TR/WCAG21/
        [Wolfe]
        Geoff Wolfe。ProPhoto RGB 色彩编码的设计与优化。2011-12-21。URL: 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]

        问题索引

        为 EdgeSeeker GMA 添加伪代码