全部文章

OKLCH人为本的色彩空间

基于 OKLCH 生成色阶,更容易组织均匀、符合人眼明暗感受的颜色变化

Shule
#OKLCH#渐变

色彩空间可以理解成一套记录和计算颜色的坐标。渐变在哪个色彩空间里计算,中间结果就会不同。

  • RGB 用红、绿、蓝三个通道描述颜色。网页里常用的 sRGB 属于这一类,Hex 是它常见的一种写法。
  • HSL 以 sRGB 为基础,用色相、饱和度和明度描述颜色。
  • OKLCH 是 OKLab 的一种表示方式,它的空间可以想象成一块不规则水晶,用 L、C、H 分别表示明度、色度和色相。

相比熟悉的 RGB,OKLCH 仍比较陌生。不过 oklch() 已写入 CSS Color Level 4,并从 2023 年 5 月起获 主流浏览器支持,可以直接用于日常 CSS。

下面对比这三种色彩空间,看看 OKLCH 的优点在哪里。

OKLCH 解决了哪些痛点

Consistent brightness

跨过色相,亮度仍然更接近

比如在 Figma 里拖动 Hue,你可能见过这种变化:明明只换了色相,黄色突然显得更亮,蓝色却沉了下去。HSL 里相同的 L,不代表人眼感受到的亮度相同;OKLCH 固定 L 和 C 后再改变 H,更容易让一组颜色保持接近的视觉重量。

只改变色相,亮度还一样吗?

HSLS 100% · L 50%去掉饱和度后,明暗起伏明显
hsl( 100% 50%)
hsl( 100% 50%)
OKLCHL 80% · C 0.20视觉重量更接近
oklch(0.8 0.2 )
oklch(0.8 0.2 )

从灰度过渡到彩色时,HSL 的明暗起伏更明显;OKLCH 固定 L,不同 Hue 的视觉重量更接近。点击带色环的 Hue 数值,可以换一组颜色继续比较

Vivid gradients

避开渐变中间的灰色脏带

灰色脏带(Gray Dead Zone)常见于 gamma 编码的 sRGB 逐通道插值。在 RGB 坐标中,这种插值是一条直线;换算到感知坐标后,它可能靠近低色度区域,让中段显得灰暗,非线性的传递函数还可能使中段偏暗。HSL 会沿色环改变 Hue,可能绕过灰色,也可能经过没有主动选择的颜色。OKLCH 能保留更多色度,同时把 Hue 路径交给我们决定。

同样的两个端点,中间可以完全不同

先看渐变差异

端点完全相同,只改变中间颜色的计算方式。

sRGB显式 sRGB 插值 · 中段变暗、变灰
Gray dead zone中段色度明显降低
OKLCH感知插值 · 色相连续、色度更完整
概念示意

两套坐标,两种插值路径

sRGB 直线属于 RGB 坐标,OKLCH 弧线属于 L/C/H 坐标;两条线共用颜色端点,但不是同一坐标系里的几何轨迹。

sRGB · RGB 坐标
逐通道的直线路径

在 R、G、B 坐标中沿直线插值;换算后,中段可能靠近低色度区域。

OKLCH · L/C/H 坐标
可指定方向的 Hue 路径

在 L、C、H 坐标中沿 Hue 方向变化,中段可以保留更多色度。

粉红青绿两条路径共用这对端点

在 CSS 渐变的方向后加上 in oklch,就能指定 OKLCH 作为插值空间;需要控制中间经过哪些颜色时,再指定 Hue 路径。

为渐变指定插值空间和 Hue 路径

garden-gradient.css
.garden-gradient {  background: linear-gradient(    90deg in oklch increasing hue,    oklch(70% 0.16 345),    oklch(74% 0.14 165)  );}

这段写法多了两个部分:

  • in oklch:让浏览器在 OKLCH 中计算两个颜色之间的过渡。
  • increasing hue:让 Hue 始终沿角度增大的方向移动。只想走较短圆弧时,可以使用默认的 shorter hue;需要经过另一侧时,也可以选择 longer huedecreasing hue
Predictable shades

交互状态的色阶更可预测

交互状态要拉开明暗,也要保留同一主色。下面用 HSL 和 OKLCH 从同一个蓝色生成四种状态,看看它们是否仍属于同一个颜色家族。

同一个蓝色,生成四种交互状态

从同一个起点向亮暗两端展开,看看四种状态是否仍像同一个颜色家族。

实际颜色去色后的感知明度
HSL用 Saturation / Lightness 生成状态浅色偏紫,深色发灰
Defaulthsl(222 100% 63%)62%
Hoverhsl(222 86% 50%)52%
Activehsl(222 48% 34%)41%
Disabledhsl(222 34% 86%)88%
OKLCH固定 H / C,主要调整 L蓝色感更连贯
Defaultoklch(0.62 0.204 264)62%
Hoveroklch(0.55 0.204 264)55%
Activeoklch(0.45 0.204 264)45%
Disabledoklch(0.86 0.041 264)86%

同一个蓝色向亮暗两端展开时,HSL 的浅色容易偏紫,深色容易发灰;OKLCH 更容易保持同一种蓝色。稳定也有边界:低色度会让 Hue 失去可见意义,色域映射也可能让 Hue 偏移,最终仍需放回真实界面检查。

为什么会有这些差异

OKLCH 对前面三个问题的改善来自两部分:LCH 提供一组更直观的调色坐标,底层的 OKLab 再让数值变化更接近人眼实际看到的差异。

LCH 结构把颜色写成三个可以分别调整的坐标:L 控制感知明度,C 控制颜色离灰色有多远,H 沿色环选择颜色。跨色相配色、渐变和交互状态里的变化,也都有了对应的调节方向。

拆开 oklch() 的三个坐标

选择坐标查看变化
oklch()
L 决定颜色看起来有多亮保持 C 和 H 不变,只移动 L,颜色会沿着明暗方向变化。
28%
42%
56%
70%
84%

再往下一层看色彩空间。OKLCH 建立在 Oklab 上,这套空间按照视觉感知重新组织颜色,让明度的数值更接近人眼看到的明暗。把同一个 sRGB 色域画成 3D 后,它在 RGB 坐标中是立方体,换到 OKLCH 坐标中则呈现不规则边界。

用 3D 看同一色域的两种坐标

跨色相

OKLCH 把明度变成一条独立的轴sRGB 没有单独的明度坐标,调亮时往往要同时移动三个通道。OKLCH 的 L 直接描述感知明度,跨过不同色相时也更接近同一视觉重量。
sRGB 空间没有独立明度轴,调亮需要一起移动多个通道
OKLCH 空间L 是独立纵轴,同一层表示相近的感知明度

怎么放进真实项目

日常写样式时,不需要记住每一组完整数值。#fff#000 这类 Hex 写法依然短而方便;主题色可以在 Token 层用 OKLCH 定义一次,组件里只读取 var(--color-action)。生成色阶、转换格式和检查色域,也可以交给下面这些工具。

用 Skill 生成并检查色阶

如果颜色已经进入项目,可以把现有主色、目标色域和需要的色阶交给 Agent,让它先生成一条可以解释、可以继续修改的 Scale。这里调用的是 OKLCH Skill:它会分配 Lightness,让色度在明暗两端自然收低,并逐级检查色域。生成之后,再放进真实界面里看看效果。

使用 oklch-skill 生成好看的色阶

npx skills add jakubkrehel/oklch-skill

已安装 oklch-skill,现在可以调用了。

/oklch-skill 以当前品牌主色为 500,生成 50–950 色阶,并输出 Tailwind v4 @theme 变量。

已生成完整的 11 阶色阶。500 保留为主色锚点;Lightness 向两端展开,每一级都按当前 L 与 Hue 的可用色度比例生成,明暗两端会自然收低。

50L 95C 0.019100L 90C 0.041200L 82C 0.079300L 73C 0.130400L 67C 0.170500L 62C 0.198600L 55C 0.175700L 48C 0.153800L 39C 0.125900L 31C 0.099950L 23C 0.074

先安装,再调用 /oklch-skill。悬停 Skill 名称可预览介绍。

这个 Skill 的作者 Jakub Krehel 还做了 oklch.fyi。网站把 OKLCH 的学习和试验放在一起,可以直接创建或转换颜色、生成色板和渐变,也能查看色域边界。想理解 Skill 在背后处理了什么,或者先用界面试几个数值,可以从这个网站开始。

不想用 AI:用 Spectr 生成并预览色阶

如果这次不想用 AI,也可以直接打开可视化工具。把一张图片交给 Spectr,它会从主色中生成 Primary 与 Gray 色阶,并把结果放进按钮、文字、登录表单和数据卡等组件里实时预览。选好方向后,可以继续调整颜色,再导出成 Tailwind CSS 色板。

总结一下,OKLCH 的原理和细节可以慢慢消化。实际写 CSS 时,可以先从一个习惯开始:在项目中优先用 oklch() 替代 rgb(),渐变时写上 in oklch。这样更容易让不同交互状态保持同一主色,渐变中段也不容易发灰。

一只浣熊摇动小机器,把发灰的色带重新变成彩色色带

推荐阅读和工具

OKLCH:以人为本的色彩空间 · GoodVibe