能动态生成衍生色,但必须用hsl(from var(--primary) h s calc(l - 12%))等含calc()的写法;hsl(from var(--primary) h s l)仅解构无变换,等价于原色,无法实现悬停、禁用等视觉反馈。

能,但必须拆解HSL通道为独立变量,且所有calc()运算必须在hsl()函数内部完成——直接写hsl(from var(--primary) h s l)只是原样复用,毫无动态性。
为什么hsl(from var(--primary) h s l)等于没写
这个语法只做“解构”,不带任何变换逻辑。hsl(from var(--primary) h s l)等价于var(--primary),所有衍生色都一模一样。悬停、禁用、深色模式全无区别,视觉反馈彻底失效。
真正起作用的是calc()对单个通道的实时运算,比如:
-
hsl(from var(--primary) h s calc(l - 12%))→ 变暗用于:hover -
hsl(from var(--primary) h calc(s * 0.4) calc(l + 10%))→ 降饱+提亮,适合禁用态文字 -
hsl(from var(--primary) calc(h + 180) s l)→ 互补色,浏览器自动模360(355+180→175)
hsl(var(--h), var(--s), var(--l))和hsl(from ...)怎么选
两者适用场景完全不同,不能混用:
立即学习“前端免费学习笔记(深入)”;
- 用
hsl(var(--h), var(--s), var(--l)):当你需要JS运行时切换主题色,比如用户点按钮换蓝/紫/绿主色,此时--h值由JS注入(210、280、120),--s和--l同步更新 - 用
hsl(from var(--base) ...):当你已有固定基础色(如--primary: #4a90e2),想纯CSS生成悬停/禁用/背景等变体,无需JS参与 - 别交叉使用:比如
hsl(from var(--primary) calc(var(--h) + 30) s l)是错的——from已解构出h,再套var(--h)多余且无效
空格、单位、引号一个都不能错
浏览器对相对颜色语法极其敏感,错即静默失效(DevTools里整条规则划掉,但不报错):
-
hsl(from var(--primary) h s calc(l - 10%))✅(from与var间有空格) -
hsl(fromvar(--primary) h s calc(l - 10%))❌(缺空格 → 整个声明被忽略) -
--sat-primary: 85%✅(带%单位);--sat-primary: 85❌(少% →hsl()拒绝解析) -
--hue-primary: 210✅(纯数字,不带deg);--hue-primary: 210deg❌(单位错) - Safari ≤16.4 不认
hsl(var(--h), var(--s), var(--l))里的空格,必须写成hsl(var(--h),var(--s),var(--l))
明度边界必须手动卡死
人眼对明度极敏感,calc(l - 25%)在基础色l: 12%时会得到负值,OLED屏上直接发灰或不可见:
- 深色模式背景明度建议卡在
4%–8%区间,低于4%易疲劳 - 文字明度下限设
max(15%, calc(l + 20%)),防黑底白字因l过低而对比崩坏 - 浅色背景主色上限封顶
92%,避免泛白刺眼:min(92%, calc(l + 30%))(需浏览器支持min()) - 禁用态推荐组合:
calc(s * 0.2)+max(65%, l),既降饱和又保可读
最易被忽略的其实是语义分层:光有--primary不够,必须配套--text-on-primary、--border-on-surface等语义变量,并在@media (prefers-color-scheme: dark)中重写全部HSL分量——只改--l会导致高饱和深色,对比度直接跌破WCAG AA标准。


















