鑫EN

关于 OKLCH

时间
2025.08.10

RGB 与 HSL 的局限

RGB 与 HSL

RGB 是由红、绿、蓝三种色光组成的加色模型,也是屏幕显示颜色的基础。在 CSS 中,它通常写作 rgb(r, g, b) 或十六进制值,例如 #RRGGBB。

RGB 适合设备计算,却不适合人直接调整。只改变 R、G、B 数值,很难预期颜色的亮度、饱和度和色相会如何变化。RGB 也不是感知一致的色彩空间:在不同区域中,相同的数值差异可能对应不同程度的视觉变化。传统 RGB 通常还以 sRGB 为边界,不能覆盖现代宽色域显示器能够呈现的全部颜色。

HSL(Hue、Saturation、Lightness)用更接近自然语言的方式组织颜色。它可以看作 RGB 的圆柱坐标变换:

  • H(Hue,色相):颜色的基本倾向,以 0–360 度表示;
  • S(Saturation,饱和度):颜色的鲜艳程度,以 0%–100% 表示;
  • L(Lightness,亮度):颜色的明暗位置,以 0%–100% 表示。
HSL 色彩空间

HSL 比 RGB 容易理解,但它的 L 并不等于人眼感知到的亮度。L 通常由 RGB 分量直接计算,没有充分反映人眼对不同色光的敏感度。同样是 50% 的 L,hsl(60, 100%, 50%) 的黄色明显比 hsl(240, 100%, 50%) 的蓝色更亮。

HSL 亮度不一致性

因此,HSL 的 L 不能直接用于统一不同色相的视觉亮度。对多个颜色同时增加 10% 的 L,也不会产生相同程度的视觉变化。这会影响色阶、对比度和颜色派生的稳定性。

共同问题

无论是 RGB、HSL 还是 HSV (Hue, Saturation, Value),它们在色彩调整和色板生成方面均面临着由感知不均匀性引发的挑战。设计者难以通过简单的数学运算或参数调整来生成视觉上和谐、亮度过渡平滑的色板。例如,在 HSL 中,若固定 L 和 S 值,仅改变 H 来生成一系列颜色,这些颜色在感知亮度上会出现明显的跳跃。更为严重的是,这种感知不均匀性对数字产品的可访问性构成了障碍。确保文本与背景之间、UI 元素之间具有足够的对比度,对于视觉障碍用户至关重要。然而,若色彩空间的亮度参数不能真实反映感知亮度,那么依赖这些参数来评估对比度的可靠性便无从谈起。

特别是 HSL 的 L 值,由于其与感知亮度的脱节,使得基于它来判断对比度往往会产生误导。HSL 的“直观性”在一定程度上是一种“虚假的便利”。它最初的出现确实是为了解决 RGB 不够直观的问题,引入了人类熟悉的色相、饱和度、亮度概念。设计者初接触 HSL 时,可能会认为其比 RGB 更易于理解和操作颜色。然而,这种“易用性”是以牺牲感知准确性为代价的。在对可访问性和视觉一致性要求日益提升的现代设计实践中,HSL 的这一根本性缺陷使其难以胜任。

感知一致颜色空间

感知一致颜色空间(Perceptually Uniform Color Space)试图让颜色之间的数值距离,与人眼感知到的差异大致对应。对参数进行相同幅度的调整,应产生相近的视觉变化。

这带来几项实际价值:

  • 可预测性:参数变化与视觉变化更接近;
  • 一致性:色板和渐变更容易保持平滑,减少 sRGB 插值中常见的灰色区域和色相偏移;
  • 设计应用:可以用更稳定的规则控制色板和视觉层级;
  • 可访问性:亮度参数更接近实际感知,可以为对比度判断提供基础。Stripe 的可访问颜色系统,就使用 CIELAB 的 L* 控制不同颜色组合的感知对比度。

CIELAB(Lab)和 LCh 是常见的感知一致色彩空间,主要用于印刷和工业颜色控制。CIELAB 包含 L*(感知亮度)、a*(红绿轴)和 b*(黄蓝轴);LCh 是它的圆柱坐标形式,包含 L*、C*(彩度)和 h(色相角)。

感知一致并不能替代视觉判断,但它使色彩系统可以更多地依赖规则,而不是完全依赖反复调校。对于多主题、明暗模式和可访问性要求,这一点尤其有用。

OKLCH

什么是 OKLCH?

Oklab 及其极坐标形式 OKLCH 由 Björn Ottosson 在 2020 年提出。它主要处理 CIELAB 在部分色相区域中感知均匀性不足的问题,并兼顾计算和数字显示场景。技术细节见 A perceptual color space for image processing。

与 CIELAB/LCH 相比,Oklab/OKLCH 改善了色相线性度和均匀性。例如,降低蓝色的彩度时,颜色会更稳定地趋向灰色,较少意外偏向紫色或绿色。

OKLCH 色彩空间

OKLCH 与 HSL 类似,也包含三个核心参数:

  • L(Lightness,感知亮度):通常为 0 到 1,分别对应黑与白;
  • C(Chroma,彩度):从 0 开始,数值越大,颜色通常越鲜艳。sRGB 中的实用上限约为 0.37,Display P3 可以更高;
  • H(Hue,色相):0–360 度的角度值。

颜色还可以包含 Alpha 透明度。

为什么是 OKLCH?

OKLCH 的主要价值包括:

  1. 感知一致性:L 的变化与感知亮度更接近,色相线性度和均匀性也优于 CIE LCH;
  2. 可预测的派生:L、C、H 相对独立。固定 H 和 C、调整 L,可以生成单色色阶;固定 L 和 C、调整 H,可以生成感知亮度较接近的多色组合;
  3. 广色域:可以表示 Display P3 等宽于 sRGB 的色域;
  4. 可访问性基础:较可靠的 L 有助于组织对比度,但最终仍应使用对应的可访问性标准验证;
  5. 可读性:OKLCH(0.7 0.15 200) 比 rgb(128, 177, 207) 或 #80B1CF 更容易读出颜色的大致结构。

CSS 支持

OKLCH 已进入 W3C CSS Color 规范,也是 Baseline 2023 Newly Available 的一部分。

CSS Color Module Level 4 引入 oklch() 和 oklab()。相对颜色语法和 color-mix() 则扩展了它在颜色派生和混合中的用途。

  • 相对颜色语法(Relative Color Syntax):从源颜色读取参数并生成变体,例如 oklch(from var(--brand-blue) calc(l * 0.8) c h)。
:root {
  --color-btn: OKLCH(45% 0.3 266);
}

.btn {
  padding: 12px;
  background-color: var(--color-btn);
  border: none;
  cursor: pointer;
  color: white;
}

.btn:hover {
  background-color: OKLCH(from var(--color-btn) calc(l + 0.2) c h);
}
相对颜色语法
  • color-mix():在指定色彩空间中混合颜色。使用 OKLCH 插值,通常可以减少 sRGB 混合中出现的饱和度下降和意外色相偏移。
.container-1 {
  background-color: color-mix(in OKLCH, yellow, blue);
  width: 20rem;
  height: 2rem;

}

.container-2 {
  margin-top: 1rem;
  background-color: color-mix(in srgb, yellow, blue);
  width: 20rem;
  height: 2rem;
}
color-mix() 函数

Linear、Stripe 和 Tailwind CSS 等产品或团队,已经在颜色系统中采用 OKLCH 或相关的感知一致方法。

使用限制

OKLCH 仍有几个需要单独处理的问题。

色域映射与溢色

OKLCH 可以定义超出设备色域的颜色。它们在 sRGB 等较窄色域中显示时,需要经过色域映射。与直接裁剪相比,在 OKLCH 中映射通常更容易保留相对亮度和色相关系,但不同浏览器和工具的结果仍可能存在差异。

浏览器实现

早期浏览器对部分边界值和转换存在渲染问题。主流浏览器已经解决大部分基础兼容性问题,但极端值和复杂色域转换仍需要跨环境测试。

浏览器适配问题

设计工具支持

Web 标准的支持已经相对成熟,但设计工具的工作流仍不统一。Figma 可以通过 P3 色域文件显示更广的颜色,也可以借助插件处理 OKLCH,但它与传统颜色格式之间仍存在转换成本。

Figma 色域支持

使用 OKLCH 构建色板

下面使用 Huetone 构建包含品牌色、成功色、警告色和错误色的色板。类似工具还包括:

先确定输出策略

项目可以采用两种方式:

  1. 直接使用:在生产环境中使用 CSS oklch(),保留相对颜色、颜色混合和广色域能力。Tailwind CSS 4 采用这种方式。若产品仍支持旧浏览器,需要单独处理兼容性。
  2. 转换输出:使用 OKLCH 设计色板,再转换为 RGB 或 HEX。兼容性更稳定,但从宽色域转换到 sRGB 时,高彩度颜色可能发生变化。

本文采用第二种方式:使用 OKLCH 构建色板,再输出传统格式。

步骤一:品牌色色板

品牌色通常在 VI 阶段确定。设计系统的设计师应尽早参与,确认它在数字产品中的可访问性和扩展空间。这里以蓝色 #0052d9 为起点。

接下来确定色阶数量。10–12 个色阶通常可以覆盖大部分场景,中性灰可能需要更多层级。

命名可以采用 Tailwind CSS 的 50, 100, 200, ..., 900, 950。与 1–9 相比,这种序列可以在已有色阶之间插入新值。本文不讨论 Primitive 与 Semantic Token 的映射。

Base Color 通常位于 500 或 600,再向明暗两侧延伸。但它的位置并不固定,需要根据自身的 L、C、H 和实际用途判断。

在 Huetone 中删除示例的其他颜色,保留一行,增加 900 和 950 两列,并把 #0052d9 放入 500。

初始色板

选择 Apply Hue to the Row,使整行使用相同的 H。随后调整 L 的分布,也可以用线性或 Cubic 函数生成初始采样。

如果品牌色本身与白色具有较高的 APCA 对比度,把它放在 500 可能无法留下足够的深色色阶。例如,#0052d9 更适合放在 800。这意味着 Base Color 不一定处于中点。若一套色板难以同时覆盖明暗主题,可以为两种主题维护独立色板,参考 Radix Colors 的方式。

将品牌色移到 800 后,再调整 L 与 C。这里让 Base Color 的 C 最高,并向两侧逐步降低,使其他色阶承担辅助作用。

亮度与饱和度调整

步骤二:应用到其他颜色

确定主色板的 L 与 C 曲线后,使用 Huetone 的 Copy Last Row 创建 Success、Warn 和 Error 色板。新行会继承所有色阶的 L 与 C。

复制后,先调整主色阶(这里是 800)的 H。例如,用绿色表示 Success。改变 H 时通常也需要微调 C,避免超出 sRGB,并保持各色板之间的视觉关系。

确定 H 与 C 后,使用 Apply Current Hue to Row 生成完整色阶。Warn 和 Error 使用相同流程。

复制上一行修改色相最终效果

此时已经得到色板的初始结构。OKLCH 和生成工具可以降低调整成本,但不能替代视觉判断。色域、相邻色阶的区分度和真实界面中的组合,仍需要手动检查。

步骤三:迭代、测试与导出

初步色板需要放回真实界面验证。使用 APCA 或 WCAG 检查文本与背景的对比度,并观察颜色组合在不同显示和光照环境中的表现。

设计、产品和研发需要共同确认业务语义、实现方式和兼容范围。色板不是独立的视觉产物,它最终需要在组件和主题中成立。

确认后,将颜色导出为 CSS Custom Properties、JSON 或设计工具需要的格式。

/* Example Palette copy color palette */

/* brand */
--brand-50: #e9eff9;
--brand-100: #e0eafb;
--brand-200: #c9daf8;
--brand-300: #acc8f9;
--brand-400: #8bb3f9;
--brand-500: #6fa0f8;
--brand-600: #4d86f0;
--brand-700: #3271e7;
--brand-800: #0052d9;
--brand-900: #0c41a2;
--brand-950: #0d2e6c;

/* success */
--success-50: #e9f1e8;
--success-100: #e0eede;
--success-200: #c9e1c6;
--success-300: #b2d1ad;
--success-400: #96c090;
--success-500: #7fb079;
--success-600: #639b5b;
--success-700: #488b3f;
--success-800: #24741a;
--success-900: #1b5913;
--success-950: #123d0d;

/* warn */
--warn-50: #f6ede4;
--warn-100: #f5e7d8;
--warn-200: #eed4ba;
--warn-300: #e9be93;
--warn-400: #e0a567;
--warn-500: #d78f3d;
--warn-600: #be781f;
--warn-700: #a6691f;
--warn-800: #88530a;
--warn-900: #674012;
--warn-950: #472c0c;

/* error */
--error-50: #f8ebe8;
--error-100: #fae4df;
--error-200: #f6cfc7;
--error-300: #f4b6a9;
--error-400: #e99c8d;
--error-500: #e28471;
--error-600: #d36450;
--error-700: #c74934;
--error-800: #ae270f;
--error-900: #871c0a;
--error-950: #601004;
最终效果

相关阅读

  1. 色彩空间与色域知识梳理及设计应用
  2. Stripe: Accessible color systems
  3. Matthew Strom: Generating Color Palettes
  4. Evil Martians: OKLCH in CSS: why we moved from RGB and HSL
  5. Björn Ottosson: A perceptual color space for image processing (Oklab)

OKLCH 的价值,在于让颜色调整更接近可计算的系统,同时保留设计师的视觉判断。它适合生成色阶、组织多主题和处理广色域,但仍需要面对色域映射、工具转换和实际对比度验证。它不是对 RGB 和 HEX 的简单替代,而是一种更适合定义和推导颜色的工作空间。