用lch()生成色阶必须固定H和C、只调L,因L是CIE明度,感知线性,确保视觉灰阶连贯;动C或H易发灰、跳色、失饱和,且兼容性差需手动降级。

直接结论:用 lch() 生成色阶,必须固定色相(H)和彩度(C),只调亮度(L)——否则视觉不连贯,尤其在青蓝/紫红区间容易发灰或跳色。
为什么只改 L 才能保证“一致的色阶”
人眼对明暗变化最敏感,L 在 lch() 中是 CIE Lightness(0% = 黑,100% = 白),感知上接近线性。同一 H 和 C 下调节 L,所有变体会落在一条视觉灰阶轴上:不会偏色、不发灰、对比度可控。一旦动 C,比如把按钮禁用态设为 lch(70% 30 240),实际在 sRGB 屏幕上可能只剩一点灰蓝;动 H 更危险——lch(60% 80 245) 看起来可能突然偏紫,尤其在浅背景上文字可读性崩坏。
常见错误现象:--color-primary-400 是 lch(60% 90 240),--color-primary-600 却写成 lch(40% 110 240),结果深色变体饱和度过高、边缘泛白、印刷或 OLED 屏上出现 banding。
lch() 色阶的参数安全范围
L 值不是越宽越好,需结合设备与用途卡死边界:
立即学习“前端免费学习笔记(深入)”;
-
L低于 15%:高彩度蓝/绿会失色,看起来像黑灰,WCAG 对比度也难达标 -
L高于 92%:泛白、刺眼,尤其在 P3 屏幕上,且与白色背景对比度跌破 1.1:1 -
C不是越大越好:LCD 屏在L=50–70区间,C > 110就易 banding;OLED 可试到 130,但必须真机验 -
H值循环安全:360° 和 0° 等价,相邻色可用calc(var(--h) + 30),但跨色相插值(如从蓝到黄)不能靠线性加减H,会经过不可控的灰带
兼容性陷阱与降级写法
lch() 在 Safari 16.4+、Chrome 111+、Firefox 123+ 支持,但旧版 Safari(如 15.x)会直接忽略整条声明,不是变灰,是“消失”——背景透明、文字继承父级色,UI 崩溃。
正确降级必须显式前置:
button {
background-color: rgb(59, 130, 246); /* fallback for old Safari */
background-color: lch(65% 85 240);
}别用 @supports (color: lch(0 0 0)) 做功能检测——部分 Safari 版本会误报支持;也别依赖 PostCSS 插件转译,lch() 的色相圆周逻辑无法被静态工具准确还原。
和 hsl() 混用时的真实代价
可以混用,但数值不“对齐”。例如 hsl(240, 100%, 50%) 和 lch(54% 0.32 240) 看起来都是蓝,但前者是 sRGB 最大饱和蓝,后者是 CIE Lab 下等感知色度蓝——在 P3 屏上,lch() 版更鲜亮;在旧 LCD 上,hsl() 版更稳定。如果主题系统里同时用两者,比如 --primary: hsl(...); --primary-light: lch(...),动画或过渡时浏览器会 fallback 到 sRGB 插值,失去 lch() 的感知均匀优势。
真正卡住人的点不是语法,而是:你得接受 lch() 色阶不是“写完就跑”,它需要真机测亮度响应、查设备色域、手动卡死 L/C 边界——没有自动适配,也没有设计师给的“标准值”可抄。


















