CSS dark mode

2019年3月30日

macOS Mojave(2018)引入了深色模式,深色模式使用一种较深的配色方案,这种配色作用于整个系统,包括 Mac 随附的应用。另外,第三方应用也可以适配这一模式。

当时 macOS Mojave 10.14.4 自带的 Safari 开始支持 CSS 草案中的 prefers-color-scheme media query,这样我们可以使网页以深色模式呈现。下文在此基础上补充了后续更常用的 JS 检测与手动主题切换写法。

CSS:prefers-color-scheme

prefers-color-scheme 用于检测用户是否希望页面使用浅色或深色外观。常见取值:

含义
light 用户偏好浅色
dark 用户偏好深色
no-preference 未明确偏好(部分环境)
1234567891011121314151617:root {  color-scheme: light dark;  --bg: #ffffff;  --fg: #111111;}@media (prefers-color-scheme: dark) {  :root {    --bg: #232325;    --fg: rgba(229, 225, 216, 0.8);  }}body {  background: var(--bg);  color: var(--fg);}

color-scheme 可提示浏览器为表单控件、滚动条等使用匹配的原生外观;在深色背景下通常建议同时设置。

也可以写浅色分支,与深色对称:

123456@media (prefers-color-scheme: light) {  body {    background: #ffffff;    color: #111111;  }}

如果不使用 Safari,或者访问不支持深色模式的网站,可以试试 Dark Reader 这款浏览器插件。

JavaScript:用 matchMedia 检测主题

CSS 负责样式,JS 则通过 window.matchMedia() 读取同一套 media query 的结果。它返回 MediaQueryList,可即时判断,也可监听变化。

检测当前是否为深色

1234567const media = window.matchMedia("(prefers-color-scheme: dark)");if (media.matches) {  console.log("当前是深色模式");} else {  console.log("当前是浅色模式");}

要点:

  • matchMedia(query):编译 media query,返回 MediaQueryList
  • media.matches:当前文档是否匹配该 query(boolean
  • media.media:传入的 query 字符串

监听系统主题变化

用户切换系统外观时,不必轮询,监听 MediaQueryListchange 即可。事件对象是 MediaQueryListEvent,同样带有 matches / media

123456789101112131415const media = window.matchMedia("(prefers-color-scheme: dark)");function onColorSchemeChange(event) {  // event.matches === true 表示偏好深色  document.documentElement.dataset.theme = event.matches ? "dark" : "light";}// 先按当前结果应用一次onColorSchemeChange(media);// 推荐:addEventListener("change", ...)media.addEventListener("change", onColorSchemeChange);// 卸载时记得移除// media.removeEventListener("change", onColorSchemeChange);

兼容说明:旧环境曾用已废弃的 addListener / removeListener;现代浏览器应使用 addEventListener / removeEventListener。若需兼容很老的 Safari,可做一层回退:

12345678function listen(media, handler) {  if (typeof media.addEventListener === "function") {    media.addEventListener("change", handler);    return () => media.removeEventListener("change", handler);  }  media.addListener(handler);  return () => media.removeListener(handler);}

与手动切换主题配合

站点常提供「浅色 / 深色 / 跟随系统」。跟随系统时用 matchMedia;手动覆盖时用 class 或 data-theme,并用 CSS 变量统一配色:

1234567891011121314151617function applyTheme(mode) {  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;  const resolved = mode === "auto" ? (prefersDark ? "dark" : "light") : mode;  const root = document.documentElement;  root.classList.remove("light", "dark");  root.classList.add(resolved);  root.style.colorScheme = resolved;  localStorage.setItem("theme", mode);}const media = window.matchMedia("(prefers-color-scheme: dark)");media.addEventListener("change", () => {  if (localStorage.getItem("theme") === "auto") {    applyTheme("auto");  }});

对应 CSS 可用 class 驱动,而不再只依赖 media query:

123456789html.light {  --bg: #ffffff;  --fg: #111111;}html.dark {  --bg: #232325;  --fg: rgba(229, 225, 216, 0.8);}

这样既能跟随系统,也能在页面内手动切换,并在系统主题变化时自动更新(仅 auto 模式)。

相关链接

评论

0 条讨论

匿名评论需填写昵称、邮箱,并通过人机验证。邮箱不会公开展示,提交后需审核通过才会显示。

还没有评论,来抢沙发吧。