纯 css 变量无法直接调用 sass 的 darken()/lighten(),因二者分属运行时与编译时;可靠方案是 hsl()+calc() 手动调整亮度值,或现代浏览器用 color-mix() 混合,js 则可实现精确色彩空间转换。

纯 CSS 变量里没法直接调用 darken() 或 lighten(),因为这些是 Sass 编译期函数,而 CSS 变量在运行时才生效——两者根本不在一个执行阶段。你写 darken(var(--color), 10%),浏览器会直接报错或忽略,连语法校验都过不去。
用 hsl() + calc() 手动模拟亮度偏移
这是目前最可靠、兼容性最好的纯 CSS 方案,核心是把颜色预先拆成 HSL 分量,再用 calc() 调整 l(亮度)值:
- 必须用
hsl()格式定义基础色,不能用hex或rgb(),否则无法提取hsl值 - 把
h和s抽成独立 CSS 变量,l单独设为可计算变量,例如::root { --color-h: 200; --color-s: 70%; --color-l: 50%; --color-primary: hsl(var(--color-h), var(--color-s), var(--color-l)); --color-primary-darker: hsl(var(--color-h), var(--color-s), calc(var(--color-l) - 10%)); --color-primary-lighter: hsl(var(--color-h), var(--color-s), calc(var(--color-l) + 10%)); } -
calc()中的百分比单位必须和原始l值单位一致(即带%),否则无效;l超出 0–100% 时浏览器会自动 clamp,但结果不可控(比如calc(5% - 10%)→-5%→ 实际取 0%,颜色发灰)
用 color-mix() 做线性混合(现代浏览器专用)
color-mix() 是 CSS Color Level 4 的新特性,不依赖 HSL 拆解,直接按比例混合颜色,语义更接近“加黑/加白”,但行为与 Sass 的 darken() 不同:
- 它在
srgb空间做线性插值,不是 HSL 亮度通道调整,对深色提暗效果更柔和,不易突变发黑 - 写法简洁:
:root { --color-primary: #3498db; --color-primary-darker: color-mix(in srgb, var(--color-primary), #000 15%); --color-primary-lighter: color-mix(in srgb, var(--color-primary), #fff 20%); } - 目前仅 Chromium 111+、Firefox 119+、Safari 16.4+ 支持;旧版浏览器会回退到 fallback 颜色(需配合
@supports或 JS 检测) - 注意:混合比例是“目标色占比”,
color-mix(in srgb, #333, #000 20%)表示 80% #333 + 20% #000,不是“变暗 20%”
为什么不能用 filter: brightness() 替代
brightness() 是作用于渲染结果的后期滤镜,它不改变颜色本身的语义值,会导致一系列连锁问题:
- 文字阴影、border-color、box-shadow 等继承自原色的属性不会被滤镜影响,造成视觉不一致
- 透明度叠加异常:对
rgba(0,0,0,0.5)应用brightness(0.8),实际是先合成再压暗,不是真正调低 RGB 值 - 无法用于伪元素生成内容(如
::before { content: "●"; color: var(--color); }),滤镜不作用于content文本颜色 - 和
contrast()grayscale()等滤镜叠加时顺序敏感,调试成本高
真要动态计算?JS 是唯一选择
如果你必须从 HEX/RGB 输入实时生成变体(比如用户选色器),CSS 变量无能为力,得靠 JS 完成 HSL 转换与 gamma 校正:
- 别用简单
parseInt(hex.substr(1,2), 16)算亮度——sRGB 需先转线性 RGB 再算 L* 值,否则浅色提亮失真严重 - 推荐用
color.js或culori这类库,它们内置了正确色彩空间转换逻辑 - JS 计算后写入
style.setProperty('--color-darker', result),确保所有用到该变量的地方实时更新 - 注意性能:避免在 scroll 或 resize 中高频调用;颜色计算本身不重,但频繁触发重绘会影响帧率
最关键的细节常被忽略:HSL 的 l 值不是感知亮度,lighten(#1a1a1a, 20%) 得到的不是 WCAG 可读的灰色,而是数学上 L=30% 的 HSL 灰——它在深色背景上可能完全不可见。真正安全的做法,是把“变暗”这件事交给设计系统约束,而不是交给函数盲目运算。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











