产品配色 2.0：使用 HCL 色彩空间替代 HSL 生成配色

目录

关于 HCL

为什么 HCL 比 HSL 更好？

TL;DR

细节

应用 HCL 到产品配色中

使用辅助工具

硬核派：HSL/RGB 转换为 HCL

硬核派：计算 HSL/RGB 的感知明度

延伸阅读

关于 HCL

HCL (Hue-Chroma-Luminance) 与 RGB 一样同属色彩空间的一种，因为最早由国际照明协会 CIE 提出，又被称作 CIELch(uv)。HCL 最普遍应用于程序生成的配色表 (color swatch) 或可视化数据图表配色。在 HCL 色彩空间中，当色相改变而 L 通道保持恒定时，对人眼而言，色彩「对比度」不变。

为什么 HCL 比 HSL 更好？

TL;DR

HSL 色彩空间中的明度 L 是相对计算机元件而言，而非人眼。L 通道与人眼对明度的感知（Luminance/Relative Luminance，后文以「感知明度」称呼）非线性匹配，HSL 中明度相等的两个颜色，人眼感知到的明度可能相去甚远（参见例子）。但产品的使用者是人类，用色彩进行标识应以人眼感知为准。

HCL 中只要两个颜色的 L 值（即感知明度）相等，颜色的对比度相等。

HCL 对颜色识别有障碍的视障人士更友好。即使有障碍，这些人能通过颜色的对比度区分颜色的不同，对比度越强，越容易区分。这里的「对比度」特指感知明度的对比。事实上在 Web 领域里，W3C 的 WCAG2.1 标准中提出了相关的可用性 (accessibility) 要求：文字前景色与背景色要达到一定程度的对比度1。

在决定配色阶段使用 HCL 色彩空间取色能够直接控制感知明度，更容易使产品配色达到较高的可用性。

上：使用HSL选取的一组同样饱和度、明度颜色，下：使用HCL选取一组同样色距、感知明度的颜色

将这两组颜色放在相同的灰度等级中

细节

首先澄清几个名词：

明度（Lightness）：指HSL色彩空间中的 L 通道，简称 L。

感知明度（Luminance，在 W3C 中又称为 Relative Luminance）：用于量化人眼对光的明度的感知。 HCL 色彩空间中的 L 通道使用的是它，简称Luma。需要与明度区分开来。

对比度：两种颜色间感知明度的对比，公式参见下文。

色距（Chroma）：HCL 中的 C 通道，可以理解为相对饱和度。

相比 HEX 或 RGB，许多设计师和前端工程师喜欢使用 HSL(Hue-Saturation-Lightness) 表示颜色，因为它的可读性更高，能够从数值中直观读出颜色的属性。如同样是三文鱼粉，HEX #fa8072 或 RGB 250, 128, 114 最多只能看出这是一个偏红的颜色， 而hsl(6, 93%, 71%)可以看出这是一个色相为 6（在色环6°位置），饱和度为 93%，明度为 71% 的颜色。如果要基于这个颜色取补色，只需要对色相数值 +180°为(186, 93%, 71%)，如果需要取一系列同色系的相近色，只需要对饱和度或明度做细微更改即可，十分方便。

HSL色相环

看上去很完美，但 HSL 有个致命的先天缺陷：它是基于 RGB2 模型创造的色彩空间。RGB 中的每个颜色是三色光通过加色法叠加而成——计算机或电视等屏幕的电子元件通过这种方法展示正确的颜色，当元件内一种色光达到极值，而另外两种色光为零时，就发出了正红、蓝或绿色光。但这并不是人眼感知光的方法。因此，HSL中两个颜色如果明度相等，只是对发光元件而言的「相等」。对人眼来说，刺眼程度却大相径庭。举例而言：

虽然亮度都标为 50%，人眼感知正绿 hsl(120, 100%, 50%) 的感知明度为 80%，而正蓝 hsl(240, 100%, 50%) 的感知明度只有 44%，差距达到了 36%。

HCL 中的 L 通道与 HSL 的 L 通道不同，是基于人眼对亮度对感知而创造的。下图解释了人眼对两种色彩空间的色环内颜色的明度是如何感知的（下方灰色部分）： HSL 的感知明度是非常不连贯的——讲人话的话就是，你在看左侧黄绿色部分时是不是觉得更刺眼，而蓝紫部分看起来温和多了？而右边色环的感知明度则始终保持连贯，视觉上更舒服。

HSL 色环与 HCL 色环在不同对比度/色度下的感知明度对比3

在实际应用中， HCL 的这一特性可以帮助各色人士更快速、友好地识别不同颜色标识和它传递的信息。

2012年桑迪飓风来袭时120小时降雨量图。左侧图片：图表实际颜色；右侧图片：去色相后图表颜色的感知明度。45

左上图为图表使用的原始配色，均使用了高饱和色，颜色间的过渡无法正确传达信息（紫色到青色代表降雨量增加还是减少了？）。经过去色处理后，也能看出整个图表颜色的感知明度并不是渐变的，时高时低。不仅是视障人士，该图表即使对没有色盲的人群而言也不够直观。

左中是使用了 HCL 理论进行配色的图表，左下方是对颜色进一步降饱和后的版本。颜色渐变暗示的信息更易懂，从右侧的去色图中也能进一步确认颜色间进行了温和过渡，并确保不同颜色的过渡传达出了正确信息。

应用 HCL 到产品配色中

使用辅助工具

事实上，已经有不少帮助你使用 HCL 进行配色的工具存在了。

取色器：

Work with Color 提供色彩的感知明度值，同时可以在不同色彩空间中做换算

Color Picker for data

HCL Wizard

W3C Working Draft - CSS Color Module Level 4 将提供内置的CSS函数lch()选取 HCL 颜色（但目前只是草稿，距离真正能用上再等10年？）

配色设计：

JS: Safe Color 我自己写的工具，根据提供的前景或背景色随机生成一个符合 W3C WCAG2.1 AA 或 AAA 标准的颜色。可以根据 string 生成固定颜色。

JS: chroma.js

Sketch: Chromatic Sketch 基于 chroma.js 的 Sketch 配色插件

Figma: Chromatic Figma 基于 chroma.js 的 Figma 配色插件

R: CRAN/colorspace

Python: colorspace

可用性检查：

Chrome 65+ devtools 在取色器里内置了符合 WCAG2.1 AA 与 AAA 标准的前、背景色对比度计算

color.review 同样提供两个颜色的感知明度对比计算。这是我想做但一直搁置，直到发现有人推出了→_→

Sketch: Cluse 同样是帮助检查颜色是否达到 AA 或 AAA 标准

图表：
数据可视化工具 d3.js 提供了使用 HCL 选色的插件

硬核派：HSL/RGB 转换为 HCL

由于 HSL 是基于 RGB 色彩模型产生，色彩空间与 RGB 一致。但 HCL 属于另一种色彩空间，两者不一定能完全匹配。具体表现为：转换后颜色但色相、感知明度均存在，但颜色的色距不一定在 HCL 色彩空间范围内。

HCL只能承载这么多颜色。纵轴为感知明度，平面圆心的角度为色相，圆半径为色距6

将 RGB 颜色转换为 HCL 需要通过 sRGB → linearRGB → CIEXyz → CIELab → CIELch，具体转换公式可以查看 W3C 给出的代码。

硬核派：计算 HSL/RGB 的感知明度

⚠️ 前方公式高能，看见公式就头疼的话还是跳过吧。

将 HSL 转换为线性 RGB (linear RGB)

当 0 ≤ H < 360, 0 ≤ S ≤ 1 and 0 ≤ L ≤ 1 时，

$$C = (1 - |2L - 1|) × S$$

$$X = C × (1 - |(H / 60°) mod 2 - 1|)$$

$$m = L - C/2$$

参照下表得出 R', G', B'的值：

$$(R',G',B') = \begin{Bmatrix} (C,X,0) & ,0&deg; <= H < 60&deg; \\ (X,C,0) & ,60&deg; <= H < 120&deg; \\ (0,C,X) & ,120&deg; <= H < 180&deg; \\ (0,X,C) & ,180&deg; <= H < 240&deg; \\ (X,0,C) & ,240&deg; <= H < 300&deg; \\ (C,0,X) & ,300&deg; <= H < 360&deg; \\ \end{Bmatrix} $$

$$(R_{8bit},G_{8bit},B_{8bit}) = ((R'+m), (G'+m),(B'+m))$$

如果最初是由 8-bit RGB 值转换为 HCL 那么从这里开始，否则无视；

$$(R_{srgb}, G_{srgb}, B_{srgb}) = (R_{8bit} / 255, G_{8bit} / 255, B_{8bit} / 255)$$

进行校正：

$$R = \begin{Bmatrix} R_{srgb} <= 0.03928 & ,R_{srgb}/12.92 \\ R_{srgb} > 0.03928 & ,((R_{srgb}+0.055)/1.055)^{2.4} \end{Bmatrix}$$

$$G = \begin{Bmatrix} G_{srgb} <= 0.03928 & ,G_{srgb}/12.92 \\ G_{srgb} > 0.03928 & ,((G_{srgb}+0.055)/1.055)^{2.4} \end{Bmatrix}$$

$$B = \begin{Bmatrix} B_{srgb} <= 0.03928 & ,B_{srgb}/12.92 \\ B_{srgb} > 0.03928 & ,((B_{srgb}+0.055)/1.055)^{2.4} \end{Bmatrix}$$

使用线性 RGB 计算颜色的感知明度

$$L_{uma} = 0.2126 * R + 0.7152 * G + 0.0722 * B$$

计算两个颜色的对比度

其中，分子中的 Luma1 是两个颜色中较浅色的感知明度，分母中的 Luma2 是较深色的感知明度。对比度范围在 1 到 21 之间，通常又写为 1:1 到 21:1。

$$C = (L_{uma1} + 0.05) / (L_{uma2} + 0.05)$$

H 保持不变，由以上可得 H, C, Luma。

延伸阅读

How To Avoid Equidistant HSV Colors by Gregor Aisch

Why HCL

本篇使用公式、图片排版、脚注、目录、html 等均由 Typlog 内置 markdown 编辑器原生支持。喜欢写文章的同学欢迎看看 Typlog。

W3C. “How to Meet WCAG (Quick Reference).” How to Meet WCAG (Quickref Reference), 4 Oct. 2019, www.w3.org/WAI/WCAG21/quickref/#contrast-minimum.↩

本文提到的 RGB 色彩空间，均指 sRGB。↩

Bednarz, Etienne. “HCL Utilities.” Art Station, www.artstation.com/artwork/ybGP9x.↩

原始图片来源: National Oceanic and Atmospheric Administration↩

Stauffer, Reto. “Why HCL.” HCL Wizard, hclwizard.org/why-hcl/. Licensed under Creative Commons Attribution 3.0 Unported License.↩

图片来源: python-colorspace↩