OKLCH in CSS: why we moved from RGB and HSL
新的CSS Color 4规范新增了用于声明颜色的oklch()
表示法。在本文中,我们将解释为什么这对设计系统和调色板很重要。
极简概述
oklch()
是定义CSS颜色的新方法。在oklch(L C H)
或oklch(L C H / a)
中,每个项目对应如下:
- L是感知亮度(0-1)。“感知”意味着它对我们的眼睛具有一致的亮度,与
hsl()
中的L不同。 - C是色度,从灰色到最饱和的颜色。
- H是色调角度(0-360)。
- a是不透明度(0-1或0-100%)。
|
|
OKLCH的优势:
- OKLCH解放了设计师,无需手动选择每种颜色。设计师可以定义一个公式,选择几种颜色,整个设计系统调色板就会自动生成。
- OKLCH可用于广色域P3颜色。例如,新设备(如苹果设备)可以显示比旧sRGB显示器更多的颜色,我们可以使用OKLCH来指定这些新颜色。
- 与
hsl()
不同,OKLCH更适合颜色修改和调色板生成。它使用感知亮度,因此不再会出现像Sass中darken()
那样的意外结果。 - 凭借其可预测的亮度,OKLCH提供了更好的可访问性。
- 与
rgb()
或十六进制(#ca0000
)不同,OKLCH是人类可读的。您可以通过查看数字快速轻松地知道OKLCH值代表的颜色。OKLCH的工作方式类似于HSL,但它在编码亮度方面比HSL更好。
OKLCH面临的挑战:
- 对于OKLCH和LCH,并非所有L、C和H的组合都会产生每个显示器都支持的颜色。尽管浏览器会尝试找到最接近的支持颜色,但使用我们的颜色选择器检查颜色仍然更安全。
- OKLCH是一个新的色彩空间。在2024年撰写本文时,其生态系统仍然有限(对于Figma,我们有插件但没有官方支持),但我们已经有了调色板生成器、颜色选择器和许多转换器。
目录:
- CSS颜色如何变化
- 比较OKLCH与其他CSS颜色格式
- OKLCH如何工作
- 如何将OKLCH添加到您的项目
- 总结结果
CSS颜色如何变化
CSS Colors Module 4
近期历史:CSS Color Module Level 4规范于2022年7月5日成为候选推荐标准。
根据MDN,截至2025年9月,oklch()
可在所有最新设备和浏览器版本中使用。
它为颜色函数添加了新的语法糖,我们将在本文中使用:
|
|
但更重要的是,CSS Color 4还增加了14种定义颜色的新方法——这些不仅仅是语法糖。这些新的颜色写入方法(如oklch()
)提高了代码可读性、可访问性,并有可能为您的网站添加新功能。
P3颜色
现代显示器实际上无法显示人眼可见的所有颜色。当前的标准颜色子集称为sRGB,它只能渲染这些人眼可见颜色的35%。
新屏幕稍微解决了这个问题,因为它们增加了30%的新颜色;这组颜色称为P3(也称为广色域)。在采用方面,所有现代苹果设备和许多OLED屏幕都支持P3颜色。所以,这不是遥远未来的事情——这正在发生。
这额外的30%颜色对设计师非常有用:
- 其中一些新颜色更加饱和。因此,您可以制作更引人注目的落地页。
- 额外的颜色为您的设计师在设计系统的调色板生成方面提供了更大的灵活性。
CSS原生颜色操作
虽然CSS Color 4确实是一大步前进,但即将到来的CSS Color 5将更加有用;它最终将为我们提供CSS中的原生颜色操作。
|
|
使用这种新语法,您可以获取一种颜色(例如,来自自定义属性)并更改颜色格式的各个组件。
但是,如前所述,这种方法有一个缺点,因为使用hsl()
格式对可访问性不利。由于不同色调的l值不同,结果将具有不可预测的亮度。
一个熟悉的 refrain 出现了:我们需要一个色彩空间,其中颜色操作产生预期结果。例如,像OKLCH。
|
|
注意:您不需要使用OKLCH来输入--accent
颜色或oklch(from …)
,但使用一致的格式对代码可读性更好。
比较OKLCH与其他CSS颜色格式
OKLCH vs. RGB/Hex
颜色格式rgb(109 162 218)
、#6ea3db
或P3模拟color(display-p3 0.48 0.63 0.84)
各自包含3个数字,代表红、绿和蓝的量。注意:color(display-p3)
中的1编码的值比RGB中的255大。
上述格式都共享 essentially 相同的问题:它们对大多数开发人员来说完全不可读。相反,人们只是像使用某种魔术数字一样使用它们,缺乏任何真正的理解或比较方式。
RGB、十六进制和color(display-p3)
不方便进行颜色修改,因为对于绝大多数人类来说,通过改变红、蓝和绿的量来直观地设置颜色是困难的。此外,RGB和十六进制也无法编码P3颜色。
另一方面,OKLCH、LCH、HSL具有我们可以设置的值,这些值更接近人们自然思考颜色的方式。OKLCH和LCH包含3个数字,分别代表:亮度、色度(或饱和度)和色调。
比较十六进制和OKLCH:
|
|
OKLCH的直观值听起来很棒,不是吗?但是,这里我们需要谈谈硬币的另一面。OKLCH的主要缺点是,与其他颜色不同,它有一个“年轻”的色彩空间,生态系统仍在开发过程中。
OKLCH vs. HSL
现在,让我们继续比较OKLCH与HSL。HSL包含3个数字来编码色调、饱和度和亮度,如下所示:hsl(210 60% 64%)
。
HSL的主要问题是它具有圆柱形色彩空间。每个色调具有相同量的饱和度(0-100%)。但实际上,我们的显示器和眼睛对于不同色调具有不同的最大饱和度。HSL通过扭曲色彩空间并扩展颜色以具有相同的最大值来隐藏这种复杂性。
因此,HSL扭曲的色彩空间不能用于正确的颜色修改;这里的L(亮度)分量不准确。不同的色调代表不同的“真实”亮度值。这导致对比度问题和糟糕的可访问性。
以下是一些真实用例示例来演示此问题:
- 增加10%亮度对蓝色和紫色会产生不同的结果。(SASS用户可能记得
darken()
如何产生意外结果。) - 色调变化(例如,使用公司的强调色产生错误类似的红色)也可能改变亮度,从而使文本不可读。
HSL不利于颜色修改。许多团队已要求社区避免使用HSL进行设计系统调色板生成。此外,与RGB和十六进制一样,HSL不能用于定义P3颜色。
OKLCH不扭曲空间;它显示真实的色彩空间及其所有复杂性。一方面,这个特性允许我们在颜色转换和P3颜色定义后具有可预测的亮度值。但另一方面,并非OKLCH中的所有数字组合都会产生可见颜色:有些仅在P3显示器上可见。但这里仍然有一些好处:浏览器将渲染最接近的支持颜色。
OKLCH vs. Oklab & LCH vs. Lab
CSS为Oklab空间提供了两个函数:oklab()
和oklch()
,Lab也是如此:lab()
和lch()
。那么,有什么区别呢?
虽然它们使用相同的空间,但它们使用不同的方式编码空间中的点。Oklab和Lab使用笛卡尔坐标(a:颜色的绿/红值,b:蓝/黄值),而OKLCH和LCH使用极坐标(角度表示色调,距离表示色度)。
简而言之,OKLCH和LCH都是开发人员可读性和颜色修改的更好选择,因为色度和色调值更接近人们实际思考颜色的方式,而不是简单的a和b。
OKLCH vs. LCH
LCH是建立在CIE LAB(Lab)空间之上的好格式,旨在解决HSL和RGB的所有问题。它可以编码P3颜色,并且在大多数情况下产生可预测的颜色修改结果。
但LCH有一个痛苦的错误:在蓝色颜色(色调值在270和330之间)中,色度和亮度变化会导致意外的色调偏移。
Oklab和OKLCH空间的创建是为了解决这个色调偏移错误。
但OKLCH不仅仅是一个错误修复,它还具有与颜色轴背后数学相关的漂亮新功能。例如,它改进了色域校正,并且CSSWG推荐使用OKLCH进行色域映射。
OKLCH如何工作
一点历史
Oklab & OKLCH色彩空间由Björn Ottosson于2020年创建。创建它们的主要原因是修复CIE LAB & LCH问题。Björn写了一篇很棒的文章,详细说明了他创建它们的原因及其实现细节。
明确地说,Oklab非常年轻,这是它的主要弱点。
但是,仅仅4年后,Oklab已经看到了非常好的采用:
- 它被添加到CSS规范中。
- Chrome、Safari和Firefox支持
oklch()
。 - Photoshop添加了Oklab用于渐变插值。
- Oklab用于调色板生成器以实现良好的可访问性。
- OKLCH已经可以通过此插件在Figma中使用。
轴
OKLCH中的颜色用4个数字编码。在CSS中,它看起来像这样:oklch(L C H)
或oklch(L C H / a)
。
以下是每个值的更详细说明:
- L是感知亮度。范围从0(黑色)到1(白色)。它也接受百分比(从0%到100%),但%在
calc()
或相对颜色中不起作用。 - C是色度,颜色的饱和度。它从0(灰色)到无穷大。实际上有一个限制,但它取决于屏幕的色域(P3颜色将比sRGB具有更大的值),并且每个色调具有不同的最大色度。对于P3和sRGB,该值将始终低于0.37。
- H是色调角度。它从0到360,经过红色20、黄色90、绿色140、蓝色220、紫色320,然后回到红色。您可以使用Roy G. Biv助记符,给每个字母大约50°。由于它是一个角度,0和360编码相同的色调。H可以带单位
60deg
书写,或不带单位60
。 - a是不透明度(0-1或0-100%)。
以下是一些OKLCH颜色的示例:
|
|
注意,某些组件可能具有none
作为值。这可能发生在颜色转换之后。例如,白色没有色调,浏览器将把none
解析为0。
|
|
CSS中的颜色修改
在CSS Colors 5中,我们将能够拥有原生颜色修改。这将突出显示OKLCH我最喜欢的一个优点:它是颜色修改的最佳色彩空间,因为它具有非常可预测的结果。
颜色修改语法如下所示:
|
|
原始颜色(上面示例中的var(--origin)
)可以是:
- 任何格式的颜色:
#ff0000
、rgb(255, 0, 0)
或oklch(62.8% 0.25 30)
。 - 具有任何格式颜色的CSS自定义属性。
from X
之后的每个组件(l、c、h)可以是:
- 一个字母(l、c、h),指示保持与原始颜色中组件相同。
- 一个
calc()
表达式。您可以使用字母(l、c、h)而不是数字来引用原始颜色中的值。 - 一个新值,它将替换组件。
它可能听起来复杂,但看一些示例可以帮助说明:
|
|
当从用户输入生成强调色时,OKLCH的预测亮度非常有用(使用我们的OKLCH颜色选择器检查示例):
|
|
色域校正
OKLCH有另一个有趣的特征:设备独立性。也就是说,OKLCH不仅仅是为当前具有sRGB颜色的显示器创建的。
您可以使用OKLCH编码任何可能的颜色:sRGB、P3、Rec2020及 beyond。一些数字组合需要P3显示器才能显示。对于其他一些组合,显示它们所需的适当显示器尚未创建。
但真的,不用担心超出色域(用户显示器支持的颜色),因为浏览器将渲染最接近的可能颜色。在另一个色域中找到最接近的颜色称为“色域映射”或“色域校正”。
这就是为什么您可以在OKLCH颜色选择器的轴上看到孔:每个色调具有不同的最大色度。不幸的是,这不仅仅是OKLCH颜色编码的问题;这是当前可用显示器和我们自身视觉感知的限制。对于某些亮度值,只有具有大色度的蓝色颜色。对于其他亮度值,绿色颜色将没有蓝色或红色中具有相同色度的对应项。
色域映射有两种方式:
- 将颜色转换为RGB(或P3)并剪裁高于100%或低于0%的值:
rgb(150% -20% 30%)
→rgb(100% 0 30%)
。这是最快的方法,但结果最差——它可能改变颜色的色调,并且这种变化对用户可见。 - 将颜色转换为OKLCH并减少色度和亮度。这保持原始色调但渲染稍慢。
CSS Colors 4规范要求浏览器使用OKLCH方法进行色域映射。但现在,Chrome和Safari使用快速但不准确的剪裁方法。这就是为什么我们目前推荐手动色域映射并在CSS中添加sRGB和P3颜色:
|
|
一些好消息:stylelint-gamut可以自动检测所有需要包装在@media
中的P3颜色。
如何将OKLCH添加到您的项目
现在所有浏览器都支持oklch()
。您不需要旧的polyfills。
步骤1:转换当前存在的颜色
您可以将所有使用十六进制、rgb()
或hsl()
格式的颜色替换为OKLCH;它们将在每个浏览器中工作。
搜索CSS源代码中的任何颜色,并使用OKLCH转换器将它们转换为oklch()
。
|
|
您也可以使用此脚本自动转换所有颜色:
|
|
如果您只有Figma文件,可以使用OkColor插件直接从Figma复制oklch()
颜色(使用OkLCH (CSS)格式)。
额外:添加调色板
也许这次小小的重构也是通过将颜色移动到调色板来提高CSS代码可维护性的好时机:
调色板的要求:
- 所有颜色都描述为CSS自定义属性。
- React/Vue/等组件仅使用这些颜色作为
var(--error)
。 - 设计师应尝试重用颜色以减少颜色变体的数量。
- 对于深色或高对比度主题,您不需要在组件CSS中使用
@media
,您只需在调色板中更改CSS自定义属性。
以下是此方法的示例。
|
|
此外,在创建调色板后,迁移到oklch()
会更容易一些。
步骤2:使用Stylelint维护OKLCH
Stylelint是一个样式linter,用于查找常见错误并推广最佳实践。它就像ESLint,但用于CSS、SASS或CSS-in-JS。
在迁移到oklch()
时,Stylelint非常有用,因为您可以:
- 指定不使用十六进制、
rgb()
、hsl()
的颜色,而是将所有颜色保持在oklch()
中以提高一致性。 - 双重检查所有P3颜色是否在
@media (color-gamut: p3)
内部,以避免浏览器色域校正(现在,Chrome和Safari没有正确执行此操作)。
让我们使用您的包管理器安装Stylelint和stylelint-gamut插件。使用NPM,运行:
|
|
创建.stylelintrc
配置:
|
|
将Stylelint调用添加到npm test
以在CI上运行它。更改package.json
如下:
|
|
运行npm test
以查找应转换为oklch()
的任何颜色。
我们还建议将stylelint-config-recommended添加到.stylelintrc
中。此Stylelint可共享配置将确保您的CSS代码使用流行的最佳实践。
额外:P3颜色
用oklch()
替换颜色将提高代码可读性和可维护性,但不会为用户添加新功能。但是,OKLCH有一个额外的功能将对用户可见:我们可以为我们的网站添加丰富的、广色域的P3颜色。例如,我们可以为落地页添加深色。
在为设计系统创建调色板时,P3颜色非常有用。然而,目前这不是非常有用,因为只有Chrome和Safari(具有正确的硬件)支持P3颜色。
以下是操作方法:
- 在您的CSS中,选择一些饱和颜色,如强调色。
- 将其复制到OKLCH颜色选择器中。
- 更改色度和亮度值以将颜色移动到P3区域。亮度图表将提供最佳反馈。只需将颜色移动到细白线上方。
- 复制结果并将其包装在
color-gamut: p3
媒体查询中。
|
|
额外:SVG中的OKLCH
您不仅可以在CSS中使用OKLCH,还可以在SVG(或HTML)中使用。例如,这对于为应用程序图标添加独特的、丰富的颜色可能很有用。
|
|
额外:渐变中的OKLCH/Oklab
渐变是色彩空间中两个或多个点之间的路径。这意味着如果您更改色彩空间,对于相同的起始和结束颜色,您将得到非常不同的渐变。
对于渐变,没有银弹;不同的任务需要不同的色彩空间。但Oklab色彩空间(OKLCH的姐妹,建立在笛卡尔坐标之上)通常具有良好的结果:
- 它中间没有像默认(sRGB)渐变那样的灰区。
- 它没有像Lab那样的蓝到紫偏移。
CSS Image 4规范具有特殊的语法来更改渐变中的色彩空间:
|
|
额外:使用OKLCH进行颜色修改
现在所有浏览器都支持来自CSS Colors 5的原生CSS颜色修改(相对颜色)。
OKLCH对于颜色修改非常出色:与HSL不同,它具有预测亮度,并且与LCH不同,它在色度变化时没有任何色调偏移问题。
不,我们不能在相对颜色内的calc
中使用%。这就是为什么我们在这里使用0.1而不是10%。
以下是您可以为按钮定义10%更暗的:hover
背景的方法:
|
|
使用CSS自定义属性,我们可以只定义一次:hover
逻辑,然后通过更改源颜色简单地创建许多变体。
|
|
由于OKLCH的可预测亮度,我们可以处理来自用户输入的颜色,并在我们的网站上具有良好的可访问性。
|
|
额外:JS中的OKLCH
使用Color.js或culori,您可以在JS中转换颜色,同时获得OKLCH色彩空间的所有好处。您可以使用OKLCH颜色选择器源代码检查culori的示例。在本文中,我将使用Color.js。
以下是一个示例,展示从自定义颜色创建强调色:
|
|
您可以使用这些库在OKLCH色彩空间中生成整个设计系统调色板。这使您能够具有预测的对比度和更好的可访问性。作为实践中的示例,Huetone(一个可访问的调色板生成器)默认使用Oklab。
总结我们为何转向OKLCH
在我们公司,我们已经在我们的项目中使用OKLCH——事实上,您正在访问的网站也使用oklch()
。所以,现在是时候问这个问题了:在迁移到OKLCH之后,我们获得了什么好处?
1. 更好的可读性
使用OKLCH,我们可以通过查看代码来理解颜色。
例如,我们可以在代码中比较暗度并找到一些与对比度相关的可访问性问题:
|
|
2. 简单的颜色修改
我们可以在代码中应用简单的颜色修改并获得可预测的结果:
|
|
3. 相对颜色
您可以使用相对颜色在CSS中定义设计系统。OKLCH是进行任何颜色自动转换的最佳色彩空间。
|
|
4. P3颜色
我们可以对sRGB和P3广色域颜色使用相同的颜色函数。
|
|
5. 与设计团队更好的沟通
由于OKLCH更接近现实生活中的颜色,在CSS中使用oklch()
实际上将教育开发人员并引导社区对颜色本身有更好的整体理解。
此外,这一举措可能产生更大的连锁反应:改善开发和设计团队之间沟通的一小步。
现代设计工具(如调色板生成器)使用Oklab以实现更好的可访问性。Figma有OkColor插件。在设计工具和开发人员CSS中使用相同的oklch()
将使每个人都在同一页面上。