暗色模式配两套颜色我算了五年,现在浏览器自己会换——`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 做暗色模式,换这套方案大概只需要一个小时,但以后每次加颜色少改一半的地方。

评论区

0 条评论

登录后可评论。

小鹿·界面实验室 349 阅读