暗色模式配两套颜色我算了五年,现在浏览器自己会换——`light-dark()` 把这件事彻底原生化了
以前做暗色模式,我第一反应是建两套 CSS,或者在 JS 里写一堆 document.body.classList.toggle('dark')——然后在每个颜色属性里写两套值,每次加新颜色都要改四个地方。
现在 Chrome 120+ 给我一条命令就搞定了。
先说结论
light-dark() 函数的语法只有两行:
:root {
color-scheme: light dark;
}
body {
color: light-dark(#333b3c, #efefec);
background-color: light-dark(#efedea, #223a2c);
}
第一个值给亮色模式,第二个给暗色模式。浏览器根据系统或用户偏好在运行时自动切换,一行 CSS 都不用写 JS。
为什么说”原生化”
以前实现暗色模式,前端团队通常走三条路:
方案一:CSS 变量 + class 切换。 定义一套变量,.dark 模式下全部覆盖。有效,但每次新增颜色值要在两个地方改,容易漏。
方案二:CSS 媒体查询 @media (prefers-color-scheme: dark)。 写在媒体查询里可以,但每个颜色都要写两遍,代码膨胀快。
方案三:Stylus / Sass 的函数封装。 写一个 darken() 或者 theme-color() 的函数,把逻辑藏在预处理器里。解决了 DRY,但换主题要重新编译,多套主题在运行时动态切换麻烦。
light-dark() 彻底省掉了这些中间层。它是 CSS 标准函数,写在浏览器引擎里,性能和 color 属性一样,没有 JS 开销,没有 class 切换的样式闪烁(FOUC)。
进阶用法:不只是颜色
light-dark() 接受任何 <color> 值,也支持 <image>,这让它的使用范围不只限于颜色主题:
/* 图片切换 */
background-image: light-dark(
url("bg-light.png"),
url("bg-dark.png")
);
/* 渐变也可以 */
background: light-dark(
linear-gradient(135deg, ghostwhite 20%, tomato),
linear-gradient(45deg, darkslategray 20%, gold)
);
/* 结合 color-mix() 做色变体 */
button {
background-color: light-dark(
color-mix(in oklch, #007acc 20%, white),
color-mix(in oklch, #007acc 30%, black)
);
}
和 @property 注册的自定义属性配合,可以做出完整的动态主题系统:
@property --brand {
syntax: "<color>";
inherits: true;
initial-value: #007acc;
}
.brand {
--brand: light-dark(#007acc, #4da6ff);
color: var(--brand);
}
浏览器支持情况
Chrome 120+ / Safari 17.5+ / Firefox 128+ 全面支持。Can I Use 数据显示全球覆盖率已超过 90%(截至 2026 年中)。Safari 16.4 打了部分支持补丁,但正式版是 17.5+。
降级方案很简单:
:root {
color-scheme: light dark;
}
body {
color: #333b3c; /* 默认亮色 */
background-color: #efedea;
}
@media (prefers-color-scheme: dark) {
body {
color: #efefec;
background-color: #223a2c;
}
}
有 prefers-color-scheme 做兜底,加了 light-dark() 的浏览器会优先用后者,没有影响。
迁移建议:怎么动手
第一步:声明 color-scheme。 在 :root 里声明支持的配色方案,这是让浏览器知道你在用双色模式的信号灯:
:root {
color-scheme: light dark;
}
第二步:替换硬编码颜色。 找出所有和主题相关的颜色属性,用 light-dark() 包起来:
/* 之前 */
text-emphasis-color: #ff4d4d;
border-color: #d9d9d9;
/* 之后 */
text-emphasis-color: light-dark(#ff4d4d, #ff8080);
border-color: light-dark(#d9d9d9, #404040);
第三步:清理 class 切换逻辑。 如果项目里有 toggle('dark') 一类的 JS 大面积切肤色的代码,可以逐步移除。浏览器自动响应的体验比 JS 控制更流畅,也避免了初始化时的闪烁。
写在最后
light-dark() 不是大功能,但它代表了一个趋势——浏览器在接管那些前端社区长期靠 Hack 解决的事情。以前我们花大量时间维护两套颜色变量、两套媒体查询、两套 class,现在这笔账被浏览器原生结清了。
动手成本极低,收益立竿见影。如果你现在还在用 class + JS 做暗色模式,换这套方案大概只需要一个小时,但以后每次加颜色少改一半的地方。
评论区
登录后可评论。