Relative Color 语法(如 hsl(from var(--primary) h s calc(l + 10)))支持运行时动态解构与重组颜色,响应深色模式、JS 主题切换及用户偏好变化;而 Sass 的 lighten() 等函数仅在编译期静态计算,无法适应运行时颜色语义变化,导致深色模式下对比度失效、维护成本高且需多套输出。

因为 hsl(from var(--primary) h s calc(l + 10)) 这类 Relative Color 语法,能在运行时实时解构、计算并重组颜色,而 Sass 的 lighten($primary, 10%) 只是编译期一次性的字符串替换——后者无法响应深色模式切换、JS 主题变更或用户偏好变化。
Relative Color 必须配合 from 和 calc() 才生效
单独写 rgb(from red r g b) 等价于写死 rgb(255 0 0),毫无动态性;真正起作用的是 from 关键字触发的“读取 → 解构 → 计算 → 合成”链路。浏览器每次渲染都会重新执行这个流程,所以只要 --primary 被 JS 修改,所有依赖它的相对颜色自动更新。
-
from后面必须跟一个可解析的颜色值(var(--x)、color-mix(...)或命名色) -
calc()是唯一允许做数值运算的地方,l + 10合法,l * 1.1也合法,但不能写lighten(...)—— CSS 里根本没这函数 - 不支持嵌套函数:不能写
hsl(from hsl(from ...)),也不能在calc()里调用max()或clamp()来约束亮度值(目前规范限制)
Sass 颜色函数在深色模式下会“反向失真”
这是最常被忽略的实际问题:lighten($primary, 10%) 在 HSL 空间里只是无条件加 l 值,当 $primary 是深蓝(l: 20%)时,加 10% 变成 30%,视觉上变亮;但若主题切换后 --primary 是浅灰(l: 90%),同样的 calc(l - 10) 就变成 80%,依然合理;而 Sass 编译出的“浅蓝”在深色背景下却可能直接发灰、失去对比度。
- 预处理器无法感知运行时颜色语义,它只认 RGB/HSL 数值,不管这个值当前属于亮色还是暗色上下文
- Relative Color 的
from表达式天然绑定到当前值,l - 10对深色是提亮,对浅色是压暗,行为一致且符合直觉 - 如果你仍用 Sass 生成颜色变量(如
$primary-light),就得为每种主题维护两套输出,体积和维护成本翻倍
JS 修改主题时,Relative Color 自动响应,Sass 颜色函数完全失效
执行 document.documentElement.style.setProperty('--primary', '#1a1a1a') 后,所有 hsl(from var(--primary) h s calc(l + 15)) 立刻重绘;而 Sass 编译出来的 .btn--primary { background-color: #3f51b5; } 永远不会变——除非你额外加载另一套 CSS 文件,引发 FOUC 或缓存失效。
立即学习“前端免费学习笔记(深入)”;
- Relative Color 不需要 JS 额外干预颜色逻辑,只需改基础变量即可
- 不能把
from当作“语法糖”来用:比如用lab(from var(--x) l a b)再calc(l * 0.9),虽然合法,但 LAB 空间下线性缩放亮度并不等同于人眼感知的“变暗”,需实测验证 - 目前仅 Chrome 130+、Firefox 135+、Safari 18.4+ 支持,旧版本需降级 fallback(例如用
color-mix(in srgb, var(--primary), white 20%))
真正难的是设计系统层面的迁移:你得把原来 Sass 里用 @each 生成的几十个色调变量(--primary-50 到 --primary-900),替换成一组语义化基础色 + 相对派生规则。这不是语法替换,而是把颜色从“静态快照”升级为“可计算的数据对象”。


















