light-dark() 不可用且功能受限,应改用 oklch() + color-mix() 动态生成浅色值,并配合 @property 实现可动画亮度调节,同时避免覆盖系统偏好、确保 OLED 显示准确与 WCAG 合规。

为什么直接用 light-dark() 不行
这个函数目前只在 Safari 17.4+ 和 Chrome 127+ 的实验性 flag 下可用(chrome://flags/#enable-css-relative-colors),且不支持旧版 Safari、Firefox 全线、Edge 126 之前版本。更关键的是:light-dark() 只能用于单个属性值,不能批量推导整套主题变量——比如你没法用它让 --bg-primary 和 --text-primary 同时按反向逻辑响应。
color-mix() + oklch() 是当前最稳的自动转浅方案
核心思路不是“深色→浅色”,而是把深色变量定义为基准,再用相对计算动态提亮/降饱和。必须满足三个硬条件:
- 所有主题变量必须基于
oklch()(不是rgb()或hsl()),否则亮度/对比度无法线性调控 - 深色变量要显式声明在
:root中,例如--bg-primary: oklch(0.15 0.02 240); - 浅色值不能手写,必须用
color-mix(in oklch, var(--bg-primary) 30%, white)这类表达式生成
示例:
code:root {
--bg-primary: oklch(0.15 0.02 240);
--text-primary: oklch(0.92 0.03 240);
}
body.darktheme {
background-color: var(--bg-primary);
color: var(--text-primary);
}
body.lighttheme {
/* 自动从深色变量“提亮”而来 */
background-color: color-mix(in oklch, var(--bg-primary) 20%, white);
color: color-mix(in oklch, var(--text-primary) 70%, black);
}用 @property 实现可动画的亮度滑动
如果想支持平滑过渡(比如拖拽亮度条),color-mix() 就不够用了——它不接受 CSS 变量作为权重参数。这时得靠 @property 声明一个可动画的数字变量:
立即学习“前端免费学习笔记(深入)”;
- 先注册:
@property --lightness { syntax: "<number>"; inherits: false; initial-value: 0; } - 再在
body.lighttheme里用:background-color: color-mix(in oklch, var(--bg-primary) calc(30% * var(--lightness)), white); - JS 控制:
document.body.style.setProperty('--lightness', '1')即可触发动画
注意:@property 目前仅 Chromium 系列完全支持,Safari 17.5+ 部分支持,Firefox 仍无计划。
别忽略系统偏好和用户手动切换的冲突点
自动转浅色逻辑只该用于“用户主动点击切换到浅色”的场景,绝不能覆盖 @media (prefers-color-scheme: light) 的原始行为。常见错误是把两套逻辑混在同一选择器里,导致:
- 系统设为深色但用户点了“切回浅色”,页面却因媒体查询优先级更高而维持深色
- 用
color-mix()计算出的浅色在 OLED 屏上发灰(因为白底本身不发光) - 按钮禁用态等语义化颜色未同步计算,造成对比度不足(WCAG 2.1 AA 失败)
真正可靠的方案是:系统偏好只做初始化,后续一切由 .lighttheme/.darktheme 类控制,CSS 变量全部走 oklch() + color-mix() 动态生成,不存静态浅色值。


















