配了八年设计系统,每次加个悬停态都要先问设计师「这个浅多少」——今天 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 oklchin oklab,不写的话浏览器默认走 sRGB,出来的结果会偏灰、偏脏。养成习惯:只要写 color-mixoklch(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%,生产可用。

评论区

0 条评论

登录后可评论。