现代浏览器支持 lch() + from 实现感知均匀暗化,比 hsl() calc() 更准确可靠;需兼容旧版时用层叠 fallback,且必须验证最终对比度。

纯 CSS 里没有 darken() 函数,但现代浏览器(Chrome 111+、Safari 16.4+、Firefox 117+)支持 lch() 和 from 关键字,可以真正模拟“感知均匀”的暗化效果——比硬调 HSL 的 L 值靠谱得多。
为什么不能直接用 hsl() + calc() 模拟 darken()
因为 hsl() 的 L 是几何亮度,不是人眼感知亮度。比如对 #3498db(一个蓝)调 hsl(h, s, calc(l - 20%)),L 值下降后色相容易挤压偏紫,且深色区域视觉变暗不明显。更糟的是:你没法把任意 HEX 色实时转成 HSL 后再算——hsl(calc(), calc(), calc()) 语法非法,浏览器直接忽略。
-
hsl()手动调整只适用于你一开始就用 HSL 定义的变量,比如--h: 204; --s: 70%; --l: 53%; - 对已存在的 HEX 或 RGB 值(如
#2c3e50),CSS 无法运行转换逻辑 - 所有
calc()运算都在声明时求值,不响应系统主题或运行时变化
用 lch() + from 实现真正可用的暗化
lch() 空间中 L 通道对应人眼感知明度,C(色度)和 H(色相)解耦,调 L 不会畸变颜色。配合 from 可以基于任意 CSS 颜色变量做相对变换:
body {
--base: #3498db;
--darker: lch(from var(--base) calc(l * 0.8) c h);
background-color: var(--base);
color: var(--darker);
}
-
calc(l * 0.8)表示“视觉上变暗到原亮度的 80%”,比darken($color, 20%)更一致 - 若需固定减量(如“减 15 个感知单位”),写
calc(l - 15);但注意L范围是 0–100,别越界 - 必须确保
--base是合法颜色值(HEX、RGB、named color 等),不能是带 alpha 的rgba()——lch(from rgba(...) ...)当前不被支持
兼容性兜底与 fallback 写法
IE 和旧版 Safari 不支持 lch() 或 from,得提供降级。不要用 @supports 包裹整个规则块(易漏样式),而是在同一声明里用层叠覆盖:
.button {
background-color: #3498db; /* fallback */
background-color: lch(from #3498db calc(l * 0.85) c h); /* modern */
}
- 现代浏览器会解析第二行并覆盖第一行;老浏览器跳过不认识的值,保留第一行
- 避免在
:root里定义--darker变量再复用——如果该变量在不支持的浏览器里被读取,后续依赖它的地方全失效 - 真要复用,用
@property声明自定义属性类型(仅 Chrome/Firefox 支持),但目前不如直接写两遍稳妥
别忽略对比度验证这一步
无论用 lch() 还是 Sass 的 darken(),输出的只是新颜色值,不保证可读。例如 lch(from #f0f0f0 calc(l * 0.9) c h) 可能产出 #e0e0e0,配白色文字对比度仍低于 4.5:1。
- 务必用浏览器 DevTools 的无障碍面板检查最终渲染结果
- 深色背景上慎用
calc(l * x),x小于 0.7 时可能过暗,文字糊成一片 - 浅色系主色(如
#e3f2fd)用lch()暗化后仍可能发灰,此时应搭配color-mix(in lch, ...)或改用mix(white, $color, 20%)(Sass)
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











