设计稿配色挺鲜艳,到了 CSS 里写成 linear-gradient 就发脏发灰——这是 CSS 渐变最常见的”翻车”。原因不在配色,在浏览器默认的插值空间。
为什么会发灰
CSS 渐变默认在 sRGB 空间做插值——也就是把两端的 RGB 值线性混合。
background: linear-gradient(90deg, #0000ff, #ffff00);
蓝 (0, 0, 255) 到黄 (255, 255, 0),中点 (127, 127, 127)——纯灰。眼睛看到的就是从蓝慢慢褪色到灰,再变成黄。
互补色对(蓝/黄、红/青、品红/绿)几乎一定中间灰。即使不是互补,只要色相跨越大,中段也会比两端”暗一截”。
OKLCH 怎么解决
oklch() 是 2023 年标准化的感知均匀色彩空间。它把颜色拆成 L(亮度)、C(彩度)、H(色相),亮度对人眼线性。
background: linear-gradient(in oklch, #0000ff, #ffff00);
加一个 in oklch,浏览器在 OKLCH 空间走色相弧线,绕开灰色。蓝→黄会经过紫红或青绿,但不再经过灰——视觉上连续、有彩度。
可选的还有:
| 颜色空间 | 特点 | 何时用 |
|---|---|---|
in srgb(默认) | 数学正确,视觉差 | 几乎不要用 |
in oklab | 感知均匀,直线插值 | 同色相区间最佳 |
in oklch | 感知均匀,色相走弧线 | 跨色相、彩色渐变最佳 |
in hsl | 老方案,亮度不均匀 | 不推荐 |
in display-p3 | 广色域,更鲜艳 | 知道目标设备支持时 |
简单结论:只要不是同一色相的明暗变化,统统用 in oklch。
色相走哪边
跨色相时浏览器默认走”短弧线”。但有时你想要走长的(比如做一圈完整色相轮):
/* 短弧线:红 → 黄经过橙 */
linear-gradient(in oklch shorter hue, red, yellow)
/* 长弧线:红 → 黄经过品红、蓝、青、绿 */
linear-gradient(in oklch longer hue, red, yellow)
/* 强制顺时针 / 逆时针 */
linear-gradient(in oklch increasing hue, red, yellow)
linear-gradient(in oklch decreasing hue, red, yellow)
conic-gradient 做色环时几乎一定要写 longer hue,否则两端会被走最短路径合并成一段。
色带是另一个问题
修了颜色空间还能看到一圈一圈的”等高线”——这是 8-bit 量化色带,跟颜色空间无关。在大面积浅色渐变上特别明显。
修法是叠一层极淡噪点:
.banner {
background:
/* 1.5% 不透明噪点 */
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>"),
linear-gradient(in oklch, #ff7e5f, #feb47b);
}
噪点不透明度 3% 以下,眼睛看不到颗粒,但色带被打散。Spotify、Stripe、Apple 大量用这招。
径向渐变的”中心点”陷阱
background: radial-gradient(circle at 50% 50%, #ff7e5f, #feb47b);
radial-gradient 默认椭圆延伸到最近角,容器宽高比变化时形状会跑——做卡片背景看起来还行,做按钮就容易拉成怪椭圆。明确写 circle 或 circle 200px 更稳。
至于 at 50% 50%:如果中心点不在容器内(比如 at -20% 50% 做一边亮一边暗),可见区域只有渐变的”一段”,颜色会看起来比预期暗——把第二个色标位置从 100% 改成 60% 就能让暗色范围限制住。
圆锥渐变做色环
background: conic-gradient(
in oklch longer hue,
red, yellow, lime, cyan, blue, magenta, red
);
这是把 OKLCH 的 H 维度可视化的最简单方式。头尾都写 red 才会闭合,不然 360° 处会有一道接缝。
调试技巧
判断哪一段在哪个空间走得对,最快的方法是给同一个容器叠两层渐变做对比:
.compare {
background:
linear-gradient(in oklch, #0066ff, #ffcc00) top / 100% 50% no-repeat,
linear-gradient(#0066ff, #ffcc00) bottom / 100% 50% no-repeat;
}
上半 OKLCH,下半 sRGB,差距一目了然。
常用配色清单
实测在 OKLCH 下不糊的几对:
| 主题 | 起 / 止 |
|---|---|
| Sunset | #ff7e5f → #feb47b |
| Ocean | #2193b0 → #6dd5ed |
| Aurora | #a8edea → #fed6e3 |
| Cosmic | #fc466b → #3f5efb |
| Mint | #11998e → #38ef7d |
这些在 sRGB 下也基本不发灰(同色相邻近),但加 in oklch 会让中段更明亮。
一句话总结
颜色发灰加 in oklch,色带加噪点;它们是两件事,要分开治。