2024 年底,CSS Color Level 4 在所有主流浏览器中全面落地(Chrome 111+、Firefox 113+、Safari 15.4+),这意味着你再也不需要 Sass 的 lighten()、darken()、mix() 了——原生 CSS 一次搞定。更关键的是,oklch 色彩空间解决了 HSL 存在了 20 多年的感知亮度不均匀问题:同样是 hsl(120, 100%, 50%)(绿色)和 hsl(0, 100%, 50%)(红色),人眼感知到的亮度差距巨大,而 oklch 让你精确控制感知亮度。
这篇文章不是科普,而是实战指南——我会用真实的设计系统案例,展示如何用 oklch + color-mix() + 相对颜色语法构建一套完整的颜色体系,彻底抛弃 Sass/Less 的颜色函数。
🎨 一、为什么 HSL 是有缺陷的色彩模型
HSL 的感知亮度问题
HSL(Hue, Saturation, Lightness)看起来很直觉:色相是角度,饱和度和亮度是百分比。但它的"亮度"(Lightness)并不是人眼感知的亮度。
看这个例子:三种颜色的 HSL Lightness 都是 50%,但你一眼就能看出绿色比蓝色亮得多:
/* ❌ HSL 的 Lightness 不等于感知亮度 */
.red { background: hsl(0, 100%, 50%); } /* 看起来暗 */
.green { background: hsl(120, 100%, 50%); } /* 看起来最亮 */
.blue { background: hsl(240, 100%, 50%); } /* 看起来暗 */
这在实际开发中导致严重问题:当你用 HSL 生成一组色阶(比如 50 到 950),绿色系和蓝色系的视觉重量完全不一致。设计系统的颜色一致性就是这么被破坏的。
oklch:感知均匀的色彩空间
oklch(Oklab Lightness, Chroma, Hue)基于人类视觉感知模型设计:
- L(Lightness):0-100,真正代表人眼感知的明暗。L=50 的任何色相,看起来一样亮
- C(Chroma):饱和度,0-0.4 左右。值越大颜色越鲜艳
- H(Hue):色相角度,0-360,和 HSL 类似
/* ✅ oklch 的 Lightness 是感知均匀的 */
.red { background: oklch(0.63 0.26 25); } /* 感知亮度一致 */
.green { background: oklch(0.63 0.29 142); } /* 感知亮度一致 */
.blue { background: oklch(0.63 0.24 264); } /* 感知亮度一致 */
💡 **提示:**oklch 的 L 值与 WCAG 对比度计算高度相关。当你需要确保文字在背景上可读时,oklch 比 HSL 更容易做出正确判断——只需要看 L 值的差值即可。
实际对比:用 HSL 和 oklch 生成色阶
下面是生成一套设计系统色阶的对比。HSL 方法需要手动调整每种色相的亮度补偿,而 oklch 只需要统一的 L 值递减:
/* ❌ HSL 色阶:需要为每种颜色手动调整 Lightness 补偿 */
--blue-50: hsl(210, 100%, 97%);
--blue-100: hsl(210, 100%, 93%);
--blue-500: hsl(210, 100%, 50%); /* 实际感知亮度偏暗 */
--green-500: hsl(142, 100%, 50%); /* 实际感知亮度偏亮 */
/* ✅ oklch 色阶:统一的 L 值,所有色相感知一致 */
--blue-50: oklch(0.97 0.02 250);
--blue-100: oklch(0.93 0.04 250);
--blue-500: oklch(0.63 0.24 250); /* 和 green-500 一样亮 */
--green-500: oklch(0.63 0.29 142); /* 和 blue-500 一样亮 */
🔧 二、color-mix():原生 CSS 颜色混合
基本语法
color-mix() 可以在任意色彩空间中混合两种颜色:
color-mix(in <color-space>, <color1> <percentage>, <color2> <percentage>)
最常用的是在 srgb 或 oklch 空间中混合:
/* ✅ 基本混合:50% 蓝色 + 50% 白色 */
.element {
background: color-mix(in srgb, blue 50%, white);
}
/* ✅ 在 oklch 空间混合:保持感知亮度均匀 */
.button-hover {
background: color-mix(in oklch, var(--primary) 85%, black);
}
/* ✅ 生成半透明色 */
.overlay {
background: color-mix(in srgb, black 60%, transparent);
}
📌 **记住:**混合时两个百分比加起来不一定要 100%。如果总和小于 100%,剩余部分由透明度填充;如果大于 100%,则按比例归一化。
实战:用 color-mix() 构建设计系统
以下是一个完整的设计系统颜色生成方案,完全用原生 CSS 实现:
/* ✅ 完整的设计系统颜色变量 */
:root {
/* 基础色 */
--primary: oklch(0.6 0.2 250);
--danger: oklch(0.6 0.25 25);
--success: oklch(0.6 0.2 145);
/* 生成 hover/active 状态色 */
--primary-hover: color-mix(in oklch, var(--primary) 85%, black);
--primary-active: color-mix(in oklch, var(--primary) 70%, black);
--primary-light: color-mix(in oklch, var(--primary) 15%, white);
--primary-subtle: color-mix(in oklch, var(--primary) 5%, white);
/* 生成文字色(保证对比度) */
--text-on-primary: color-mix(in oklch, var(--primary) 10%, white);
--text-secondary: color-mix(in oklch, var(--text) 60%, transparent);
}
/* 使用 */
.button {
background: var(--primary);
color: var(--text-on-primary);
border: 1px solid color-mix(in oklch, var(--primary) 30%, transparent);
}
.button:hover { background: var(--primary-hover); }
.button:active { background: var(--primary-active); }
这比 Sass 的方案更好,因为:
| 对比维度 | Sass/Less | CSS color-mix() |
|---|---|---|
| 运行时可变 | ❌ 编译时固定 | ✅ 运行时动态计算 |
| CSS 变量联动 | ❌ 需要 mixin | ✅ 直接用 var() |
| 主题切换 | ❌ 需要重新编译 | ✅ 改变量即可 |
| 浏览器支持 | ✅ 全部 | ✅ 2024+ 全部 |
| 构建依赖 | ⚠️ 需要预处理器 | ✅ 零依赖 |
暗色模式的最佳实践
color-mix() 在暗色模式中特别强大——你不需要维护两套颜色变量:
/* ✅ 一套变量同时支持亮色和暗色模式 */
:root {
--base: oklch(0.6 0.2 250); /* 主色 */
--bg: white;
--text: oklch(0.15 0 0);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: oklch(0.13 0.02 250); /* 暗色背景带一丝主色 */
--text: oklch(0.9 0.01 250); /* 亮色文字带一丝主色 */
}
}
.card {
/* 自动适应亮色/暗色模式 */
background: color-mix(in oklch, var(--bg) 95%, var(--base));
color: var(--text);
border: 1px solid color-mix(in oklch, var(--base) 20%, var(--bg));
box-shadow: 0 2px 8px color-mix(in oklch, var(--base) 10%, transparent);
}
⚡ 三、相对颜色语法:最强大的 CSS 颜色操作
基本语法
相对颜色语法(Relative Color Syntax)让你基于一个现有颜色,修改其中某个分量:
/* 基于 --primary,只修改亮度 */
color: oklch(from var(--primary) 80% c h);
/* 基于 --primary,增加饱和度 */
color: hsl(from var(--primary) h calc(s + 20%) l);
/* 基于 --primary,降低透明度 */
color: rgb(from var(--primary) r g b / 50%);
from 关键字后面的变量被解构为各个分量(l、c、h 或 r、g、b),你可以在 calc() 中自由操作它们。
实战:用相对颜色语法替换 Sass 函数
下面展示如何用相对颜色语法完全替代 Sass 的颜色函数:
/* ❌ Sass 写法 */
$primary: #3b82f6;
.btn:hover { background: lighten($primary, 10%); }
.btn:active { background: darken($primary, 10%); }
.badge { background: transparentize($primary, 0.8); }
/* ✅ CSS 相对颜色语法 */
:root { --primary: oklch(0.6 0.2 250); }
.btn:hover {
/* 增加感知亮度 10% */
background: oklch(from var(--primary) calc(l + 0.1) c h);
}
.btn:active {
/* 降低感知亮度 10% */
background: oklch(from var(--primary) calc(l - 0.1) c h);
}
.badge {
/* 降低透明度到 20% */
background: oklch(from var(--primary) l c h / 20%);
}
⚠️ **警告:**oklch 的 L 值范围是 0-1,不是百分比。
calc(l + 0.1)表示增加 10% 的感知亮度。用clamp(0, calc(l + 0.1), 1)来防止溢出。
高级模式:基于用户颜色生成完整色板
这是一个真实场景:用户可以选择主题色,你需要基于这个颜色自动生成 hover、active、focus 等所有状态色:
/* ✅ 基于用户选择的主题色,自动生成完整色板 */
.theme-generator {
--user-color: oklch(0.6 0.2 200); /* 用户选择的颜色 */
/* 自动生成 10 级色阶 */
--color-50: oklch(from var(--user-color) 0.97 calc(c * 0.15) h);
--color-100: oklch(from var(--user-color) 0.93 calc(c * 0.25) h);
--color-200: oklch(from var(--user-color) 0.87 calc(c * 0.4) h);
--color-300: oklch(from var(--user-color) 0.78 calc(c * 0.6) h);
--color-400: oklch(from var(--user-color) 0.69 calc(c * 0.8) h);
--color-500: var(--user-color);
--color-600: oklch(from var(--user-color) 0.52 calc(c * 0.9) h);
--color-700: oklch(from var(--user-color) 0.42 calc(c * 0.85) h);
--color-800: oklch(from var(--user-color) 0.33 calc(c * 0.7) h);
--color-900: oklch(from var(--user-color) 0.24 calc(c * 0.55) h);
--color-950: oklch(from var(--user-color) 0.15 calc(c * 0.4) h);
/* 自动生成状态色 */
--hover: oklch(from var(--user-color) calc(l - 0.06) c h);
--active: oklch(from var(--user-color) calc(l - 0.12) c h);
--ring: oklch(from var(--user-color) l c h / 40%);
--subtle: oklch(from var(--user-color) 0.96 calc(c * 0.2) h);
}
这段代码的核心洞察是:在 oklch 空间中,降低 Chroma(c * 0.15)的同时保持高 Lightness,就能自动生成柔和的浅色版本——这在 HSL 中很难做到,因为 HSL 的 S 和 L 是耦合的。
📊 四、色彩空间选择指南
不同的混合场景适合不同的色彩空间。选择错误的色彩空间会导致混合结果发灰或过饱和:
| 色彩空间 | 混合效果 | 适用场景 | 代码示例 |
|---|---|---|---|
srgb |
线性混合,简单直观 | 半透明叠加、遮罩 | color-mix(in srgb, red 50%, blue) |
oklch |
感知均匀,不会发灰 | 设计系统色阶、状态色 | color-mix(in oklch, red 50%, blue) |
oklab |
类似 oklch,笛卡尔坐标 | 需要数学运算时 | color-mix(in oklab, red 50%, blue) |
hsl |
色相插值,可能发灰 | 旧代码兼容 | color-mix(in hsl, red 50%, blue) |
lch |
感知均匀,但色相不稳定 | 不推荐新项目 | color-mix(in lch, red 50%, blue) |
⚡ **关键结论:**90% 的场景应该用
oklch。只有在叠加半透明颜色(遮罩、阴影)时用srgb,因为半透明叠加本身就是线性操作。
💡 五、避坑指南与浏览器兼容
常见陷阱
陷阱 1:oklch 的 Chroma 值超出色域
oklch 允许你写出超出 sRGB 色域的颜色(高 Chroma + 某些 Hue 组合)。浏览器会自动裁剪(clamp)到最近的可显示颜色,但这可能导致不同浏览器显示不一致:
/* ⚠️ 可能超出 sRGB 色域 */
.element {
color: oklch(0.7 0.35 300); /* 高饱和紫色,可能被裁剪 */
}
/* ✅ 安全做法:使用 color() 函数指定色域 */
.element {
color: oklch(0.7 0.35 300);
color: color(display-p3 0.8 0.3 0.9); /* 在 P3 显示器上精确显示 */
}
陷阱 2:color-mix() 不支持旧版 Safari
Safari 15.4+ 支持 color-mix(),但 15.0-15.3 不支持。如果你需要支持这些版本:
/* ✅ 带降级方案的 color-mix() */
.button {
background: #3b82f6; /* 降级 */
background: color-mix(in oklch, var(--primary) 85%, black);
}
陷阱 3:相对颜色语法中 calc() 的精度
oklch 的 L 值是 0-1 的小数,calc() 运算时注意精度:
/* ❌ 可能溢出 */
background: oklch(from var(--color) calc(l + 0.15) c h);
/* ✅ 用 clamp 确保安全 */
background: oklch(from var(--color) clamp(0, calc(l + 0.15), 1) c h);
浏览器支持现状(2026 年 5 月)
| 特性 | Chrome | Firefox | Safari | 全球覆盖率 |
|---|---|---|---|---|
| oklch() | 111+ ✅ | 113+ ✅ | 15.4+ ✅ | ~95% |
| color-mix() | 111+ ✅ | 113+ ✅ | 16.0+ ✅ | ~93% |
| 相对颜色语法 | 119+ ✅ | 128+ ✅ | 16.4+ ✅ | ~88% |
💡 **提示:**如果你的项目需要支持 2023 年之前的浏览器,用 PostCSS 插件
postcss-oklab-function做降级转换。但对于 2025 年之后的新项目,可以放心使用全部三个特性。
✅ 总结与迁移建议
CSS Color Level 4 不是渐进式改进,而是范式转换。oklch 让颜色操作变得可预测、可计算;color-mix() 和相对颜色语法让你在运行时动态操作颜色,这是 Sass 做不到的。
迁移策略:
- ✅ 新项目直接使用 oklch 作为默认色彩空间,不再写 HSL
- ✅ 用 CSS 自定义属性 + color-mix() 替代 Sass 的颜色 mixin
- ✅ 用相对颜色语法替代
lighten()、darken()、transparentize() - ❌ 不要一次性迁移旧项目——在新增组件时逐步引入
- ❌ 不要在需要支持 IE/旧浏览器的企业项目中使用(除非有 PostCSS 降级)
推荐工具:
- OKLCH Color Picker — 可视化选择 oklch 颜色
- CSS Color Mixer — 交互式 color-mix() 实验
- jsjson.com JSON 格式化工具 — 格式化你的设计 token JSON 文件
- Color.js — JavaScript 色彩空间转换库