现代 CSS 中的 OKLCH 颜色:为什么它能赢,以及如何落地
一份实战指南,讲解 CSS 中的 OKLCH 颜色。为什么 hex 在规模化时失灵,OKLCH 如何修复明度漂移,四步迁移路径,以及每个设计系统在 2026 年都应该交付的感知均匀色阶。

现代 CSS 中的 OKLCH 颜色:为什么它能赢,以及如何落地
OKLCH 是 CSS 交付的第一个设计师真正能够推理的色彩空间。仍在用 hex 构建色板的团队,正在悄悄输掉每一场他们甚至不知道自己卷入的对比度和暗色模式之争。
Hex 从来不是色彩空间,它只是一种序列化格式
Hex 是 sRGB 的简写。#4A90E2 不是一个颜色规范,它只是把红、绿、蓝三个通道的值压缩成六个字符。sRGB 是为 1990 年代初的 CRT 显示器设计的,不是为人类视觉感知设计的。
后果就是:两个 hex 值在你色板里可能处于相同的数值“亮度”位置,但在你眼里看起来完全不同。在大多数设计系统里,blue-500 看起来比 yellow-500 暗,哪怕它们在色阶中的相对位置一模一样。这不是品味问题,而是烙在坐标系里的数学问题。
“Hex 从来不是色彩空间,它只是一种序列化格式,而我们从那以后一直在为明度争论不休。”
HSL 假装修复了问题,其实没有
HSL 出现后给了设计师一套语言:色相、饱和度、明度。它感觉像一个系统,但并不是。HSL 的明度通道底层仍然映射到 sRGB 值,这意味着 HSL 继承了 hex 的所有感知问题。
hsl(220, 70%, 50%) 和 hsl(45, 70%, 50%) 名义上明度相同。把它们并排打开看看,黄色明显更亮。HSL 只是给了你更好听的词,来描述同一套有问题的数学。
OKLCH 是感知均匀的
OKLCH 建立在 OKLab 色彩空间之上,由 Björn Ottosson 于 2020 年开发,专门用来解决早期色彩空间感知不均匀的问题。OKLCH 中的 L 代表感知明度,按人类视觉系统对光的真实响应来校准。
OKLCH 中相等的 L 值意味着相等的感知亮度。不是大致相等,不是“色度相近时通常相等”,就是相等。这是此前任何 CSS 色彩空间都无法提供的特性,它改变了你推理色板生成的全部方式。
OKLCH 获胜的三个理由
感知均匀的明度、可预测的色相插值,以及广色域支持。每一项都修复你当前色板中的一个具体 bug,合在一起,它们构成一套可以跨主题、组件和显示设备扩展的色彩工作流。

理由一:明度漂移已死
在 hex 和 HSL 系统中,明度漂移是你为每个色板默默缴纳的隐形税。你凭肉眼把 blue-50 和 amber-50 调到看起来合适,它们在不同屏幕上就会分道扬镳,在暗色模式里互相打架,对比度的计算也始终对不上。
在 OKLCH 里,blue-50 和 amber-50 的明度数值相同,而这个数值代表相同的感知亮度。Tailwind v4 正是出于这个原因把整个色板迁移到了 OKLCH。如果你使用 Radix UI 的色阶,即使从未在文档里见过 OKLCH 这个词,你也已经在受益于感知均匀性。
理由二:色相插值终于正常了
使用 sRGB 插值的 CSS 渐变,走的是 RGB 色彩立方体,而不是感知空间。sRGB 中从蓝到黄的渐变会穿过一个浑浊的灰色中段,因为 RGB 对角线会穿过无彩色轴附近。这不是艺术效果,而是几何意外。
OKLCH 渐变插值沿感知色环转动色相角。OKLCH 中从蓝到黄会经过青和绿,这正是你的眼睛所期待的。灰色浑浊消失了。这对 UI 渐变、动画色彩过渡,以及任何生成中间色阶的色板工具都很重要。
理由三:P3 和 Rec2020 触手可及
Hex 无法描述 sRGB 之外的颜色。2016 年以来的每一部 iPhone、每一台配备广色域面板的现代笔记本,以及大多数新的消费级显示器,都能显示存在于 P3 色域但没有 hex 地址的颜色。你一直把饱和鲜艳的颜色扔在桌上不拿,只因为你的坐标系叫不出它的名字。
OKLCH 可以原生描述 P3 和 Rec2020 颜色。oklch(0.60 0.28 260) 是一种存在于 P3 但超出 sRGB 色域的鲜艳蓝。现代浏览器知道怎么处理它,Apple 的 Display P3 色彩指南也已多年指向这个方向。现在迁移到 OKLCH 的设计系统,也就是 P3 成为默认预期时已经准备好的设计系统。
从 hex 到 OKLCH 的四步迁移
审计、转换、重新生成、再审计。对基础令牌来说迁移是机械的,对语义令牌则需要判断。一个中等规模设计系统的完整迁移只需几天,而不是几周。开始之前先把新令牌锚定在清晰的 Figma variables 架构中,最能省时间。
第一步:审计现有色板
导出系统中的每一个颜色令牌。用 oklch.com 或 PostCSS OKLCH 插件之类的工具把每个 hex 值转换为 OKLCH。按 L 值排序,横向查看各色相的明度列。
问题会立刻浮现。Blue-300 的 L=0.72,而 yellow-300 的 L=0.87。本该具有同等视觉重量的令牌,差得很远。这次审计就是证据,让你能轻松地向任何反对者证明迁移决定的合理性。
第二步:把基础令牌转换为 OKLCH
拿出基础色阶,也就是原始的编号色阶,而不是语义令牌,然后转换为 OKLCH。把明度值统一取整到所有十一档一致的步长:0.97、0.93、0.86、0.75、0.65、0.54、0.45、0.37、0.27、0.19、0.13。
不要试图精确保留原来的 hex 值。重点是修复不一致。转换后的 blue-500 和 red-500 不会与旧值逐像素相同,而这正是全部目的。系统是在变好,而不仅仅是变得不同。
第三步:重新生成色阶
有了干净、一致的明度步长,跨色相重新生成色阶就变成了一次表格操作。设定 L 列,为每个色相选择色度(蓝色在被裁切前通常比棕色和黄色承载更多色度),调整 H 列,然后生成。
结果是,blue-500 和 red-500 在你的系统里第一次有了相同的视觉重量。以前凭主观判断的品牌色板决策,变成了有坐标可指的有原则的选择。
第四步:重新审计无障碍
新色板,新一轮对比度检查。OKLCH 让对比度预测更容易,因为明度现在名副其实。把两个已知 L 值的令牌配对,在你打开检查器之前就能对它们的对比度有个可靠的预判。
对所有语义令牌配对运行 WCAG 和 APCA 对比度检查。标出不再达标的配对,并调整未通过令牌的色度或明度。对 OKLCH 色板做一次规范的无障碍色彩对比度检查,通常比对 hex 色板做同样检查惊喜更少,因为明度步长以前是错的,现在不是了。
想要一套在各种模式、色相和现代显示器上都稳得住、没有明度漂移的色彩系统吗?Brainy 设计 OKLCH 原生色板系统,以 Figma variables、CSS 自定义属性和 Tailwind 令牌的形式交付,对比度检查已经做完。
OKLCH 的四个坑
超出色域的裁切、色度裁切、alpha 处理和工具链缺口。每一个都会咬到刚接触 OKLCH 的团队,而每一个都有具体的修复方法,知道该找什么之后几分钟就能搞定。
| 坑 | 会发生什么 | 修复 |
|---|---|---|
| 超出色域裁切 | 高色度值落在 sRGB 之外,被浏览器裁切,有时会改变色相 | 用 @media (color-gamut: p3) 把仅限 P3 的颜色包起来 |
| 色度裁切 | 裁切不只改变饱和度,还会轻微改变色相 | 为获得广泛兼容,将 sRGB 安全色度保持在约 0.20 以下 |
| Alpha 处理 | oklch(0.55 0.20 260 / 0.5) 在静态填充中可用;旧浏览器中某些插值路径会出问题 | 在渐变和过渡中测试 alpha,而不只是静态颜色 |
| 工具链缺口 | Figma 原生取色器不直接暴露 OKLCH | 使用 Tokens Studio,或在 CSS 中定义令牌并通过 Figma 的代码面板引用 |
色域问题咬得最狠。当你写下 oklch(0.60 0.32 260),这个色度值已经超出 sRGB。桌面浏览器的处理方式是裁切到最接近的 sRGB 颜色,这会改变表观色相。修复方法要么是基础令牌保持在 sRGB 安全色度内,要么把广色域值用媒体查询包起来,并在上方保留一条回退声明。
可用的 OKLCH 色阶
一个实用的 11 档色阶,明度步长一致,以色相 260 的蓝色为参考。明度步长在你系统中的每个色相上都完全相同。每个色族只有色度和色相会变。
:root {
--blue-50: oklch(0.97 0.02 260);
--blue-100: oklch(0.93 0.05 260);
--blue-200: oklch(0.86 0.09 260);
--blue-300: oklch(0.75 0.14 260);
--blue-400: oklch(0.65 0.18 260);
--blue-500: oklch(0.54 0.22 260);
--blue-600: oklch(0.45 0.20 260);
--blue-700: oklch(0.37 0.17 260);
--blue-800: oklch(0.27 0.13 260);
--blue-900: oklch(0.19 0.09 260);
--blue-950: oklch(0.13 0.06 260);
}
要生成红色(色相 25)、琥珀色(色相 75)或绿色(色相 145),让全部十一档的 L 列保持一致。根据色相的天然色度容量调整 C:黄色和绿色在被裁出 sRGB 之前承载的色度比蓝色少。把 H 调到你的目标色相。这就是整个色板系统。
建立在这套色阶之上的暗色模式色彩系统使用语义令牌,按 L 步长和色相在基础令牌之间切换,而不是一个一个手动覆盖 hex 值。

浏览器支持与安全迁移窗口
自 2023 年起发布的每个主流浏览器都支持 OKLCH。Chrome 111、Safari 15.4、Firefox 113 和 Edge 111 都提供完整的 OKLCH 支持。截至 2025 年中,全球覆盖率超过 93%。迁移窗口已经打开。
| 浏览器 | OKLCH 支持 | 起始版本 |
|---|---|---|
| Chrome | 完整 | 111(2023 年 3 月) |
| Safari | 完整 | 15.4(2022 年 3 月) |
| Firefox | 完整 | 113(2023 年 5 月) |
| Edge | 完整 | 111(2023 年 3 月) |
| Samsung Internet | 完整 | 21(2023) |
对任何不支持 OKLCH 的浏览器,回退只需一行。先声明 hex 回退,再声明 OKLCH 值。不理解 OKLCH 的浏览器会忽略它并使用 hex,支持的浏览器则使用更好的值。
color: #4a7de8; /* sRGB fallback */
color: oklch(0.54 0.22 260); /* OKLCH for modern browsers */
Tailwind v4 原生以 OKLCH 生成色板。Linear、Stripe 的 Sail 设计令牌,以及 Apple 的开发者色彩指南,都已转向感知均匀的系统。行业已经落地。问题只是你的团队何时加入。
FAQ
OKLCH 难学吗?
这个坐标系和 hex 或 HSL 不同,但并不复杂。L 是 0 到 1 的明度。C 是色度,大致相当于饱和程度。H 是以度为单位的色相。一旦你内化了 L 代表感知亮度、C 代表色彩丰富度,这个系统会比当年的 HSL 更快变得直观。
我今天能在 Figma 里用 OKLCH 吗?
截至 2025 年,Figma 的取色器没有原生暴露 OKLCH。实用的工作流是:通过 Tokens Studio 在令牌文件中用 OKLCH 定义令牌,或者用同步到 Figma variables 的 CSS 变量文件,然后用 Figma 的代码导出来验证数值。Figma 的渲染引擎会在内部处理转换。
OKLCH 会破坏我现有的 hex 值吗?
不会。你可以逐个令牌、渐进地引入 OKLCH。现有的 hex 值会和以前一样继续工作。迁移是叠加式的,不是替换。
LCH 和 OKLCH 有什么区别?
LCH 基于 CIELAB 色彩空间,它是感知均匀的,但存在色相线性问题,尤其在蓝紫范围。OKLCH 基于 OKLab,修正了这些色相偏移。对实际设计工作来说,OKLCH 是更好的选择,因为它的色相插值惊喜更少。
如何把 hex 转换为 OKLCH?
使用 oklch.com、PostCSS oklch 插件,或 CSS Color 4 规范的 playground。粘贴你的 hex 值,读取 OKLCH 坐标,把 L 和 C 取整到两位小数,把 H 取整到最近的整数度。这就是你可用的令牌值。
OKLCH 会影响无障碍要求吗?
WCAG 对比度要求是基于 sRGB 相对亮度定义的,而不是 OKLCH 明度。APCA 更贴近感知模型,与 OKLCH 的 L 值映射更自然。实际上,OKLCH 让对比度预测更直观,因为 L 跟踪感知亮度,但你仍然需要把语义令牌配对送进对比度检查器。流程不变,只是提前推理起来更容易。
OKLCH 真正解锁的转变
OKLCH 不是潮流。这是 CSS 第一次交付一个围绕人眼感知颜色的方式设计的色彩空间,而工具链已经追上来,任何愿意投入几天专注时间的团队,今天就可以实际迁移。
现在行动的团队一次得到三样东西:无需手动覆盖就能在暗色模式下稳住的色板,与坐标系一致的对比度表现,以及无需未来重写就能延伸到 P3 的基础。观望的团队仍将继续争论为什么 blue-300 看起来比 orange-300 重,而答案依然不变。这个坐标系从来不是为那项工作设计的。
如果你想在这套色板之上构建完整的令牌架构,Figma variables 架构那篇文章涵盖了语义层。至于这项工作在暗色模式中的应用,暗色模式色彩系统从本文结束的地方接着讲。
想让我们替你做好?雇用 Brainy。
Want a color system that holds up across modes, hues, and modern displays without lightness drift? Brainy designs OKLCH-native palette systems that ship as Figma variables, CSS custom properties, and Tailwind tokens with the contrast pass already done.
Get Started




