配了八年设计系统,每次加个悬停态都要先问设计师「这个浅多少」——今天 CSS 用两行把自己会算了
配了八年设计系统,每次加个悬停态都要先问设计师「这个浅多少」——今天 CSS 用两行把自己会算了
做设计系统的工程师,大概都被这种对话折磨过:设计师说「这个按钮悬停比正常浅 15%」,你打开 Sass 文件,找到 $btn-bg-lighten,改掉,重新编译。下一个需求来了:「主色的 10% 透明度用在蒙层上」,你又得开一个新的 Sass 变量。颜色一多,整个 _colors.scss 就变成了一张没人敢动的蜘蛛网。
这套流程的根源是 CSS 长期以来没有能力表达「这个颜色和那个颜色是什么关系」——它只能存最终值,不能存计算过程。所有派生都得靠 Sass/LESS 的函数,或者靠 JS 在运行时算。
浏览器原生颜色数学,现在有三件套
CSS Color Level 5 给了我们三个互相配合的能力,组合起来可以彻底取代 Sass 的颜色函数。
第一件是 OKLCH。传统的 RGB 和 HSL 都不是感知均匀的颜色空间——同样是亮度 +10%,从暗到中亮看起来差别大,从中亮到亮看起来差别小。OKLCH 用更符合人眼感知的模型,让你调出来的渐变和色阶「看起来均匀」:
:root {
--brand: oklch(65% 0.25 260);
}
第二件是相对颜色语法(Relative Color Syntax),这是核心。你拿一个已有颜色,暴露它的通道,做运算,生成新颜色:
:root {
--brand: oklch(65% 0.25 260);
--brand-light: oklch(from var(--brand) calc(l + 0.15) c h);
--brand-dark: oklch(from var(--brand) calc(l - 0.15) c h);
--brand-muted: oklch(from var(--brand) l calc(c * 0.6) h);
}
from var(--brand) 把 --brand 的 L、C、H 三个通道带进作用域,然后 calc() 自由运算。全程没有 JS,没有 Sass 编译,没有构建步骤,CSS 文件直接读得懂。
第三件是 color-mix(),在两个颜色之间按比例混合,适合做状态变体(正常/悬停/按下三档):
:root {
--surface: oklch(95% 0.02 260);
--surface-hover: color-mix(in oklch, var(--surface) 85%, white);
}
注意这里的 in oklch——在感知均匀空间里混合,结果比在 sRGB 里混好看很多。
三件套的真实项目用法
举一个具体场景:主题色 + 暗色模式,全部用 CSS 表达,不需要任何 JS class 切换。
:root {
--brand: oklch(65% 0.25 260);
--brand-hover: oklch(from var(--brand) calc(l + 0.1) c h);
--brand-active: oklch(from var(--brand) calc(l - 0.08) c h);
--text-on-brand: oklch(from var(--brand) calc(l + 0.45) c h);
}
@media (prefers-color-scheme: dark) {
:root {
--brand: oklch(70% 0.2 260);
--brand-hover: oklch(from var(--brand) calc(l + 0.08) c h);
}
}
改变 --brand 一个值,下面所有派生颜色自动更新。没有 Sass 变量,没有 JS 主题注入,没有 CSS-in-JS 的运行时成本。
三个坑,踩过的才知道
第一,浏览器缺口。相对颜色语法 Safari 16.4 开始支持,Chrome 119+,Firefox 128+。如果你还要兼容再老的版本,用 @supports 做渐进增强:
.button {
background: var(--brand);
}
@supports (color: oklch(from red l c h)) {
.button:hover { background: oklch(from var(--brand) calc(l + 0.1) c h); }
}
第二,OKLCH 值要自己算。Figma 和大多数设计工具目前还不直接输出 OKLCH,可以用这个网站把 hex 转过去:oklch.com。迁移成本是一次性的。
第三,颜色空间选错了等于白干。混色和派生都要指定 in oklch 或 in oklab,不写的话浏览器默认走 sRGB,出来的结果会偏灰、偏脏。养成习惯:只要写 color-mix 或 oklch(from ...),第一件事补上颜色空间。
下一步,从哪里开始
如果你现在还在用 Sass 的 lighten() / darken() 做颜色派生,找一个非关键的 token (比如边框色、背景色),把它从 Sass 变量迁移到 OKLCH + 相对颜色语法。跑一遍验收,改法一模一样,但构建产物里少了 3~5 行 Sass 函数调用。
等你对这个模式建立手感,再把主色迁移过来。那时候你就会发现,设计系统改主题色再也不需要 grep 全局了——一个 :root 的 --brand 改了,下面全部自动长出来。
以上三件套的浏览器支持数据(2026-09):OKLCH 色彩函数 Chrome 111+/Firefox 113+/Safari 16.2+,相对颜色语法 Chrome 131+/Firefox 133+/Safari 18+,color-mix() Chrome 111+/Firefox 113+/Safari 16.2+。全球覆盖率均超过 93%,生产可用。
评论区
登录后可评论。