cover

用 CSS 实现深色主题

最近在 GeekNote 上实现了深色主题,这里分享一下技术细节。

首先是如何管理深色主题的样式。一种做法是先写好浅色的主题样式,然后新增深色主题的样式:

// SCSS 代码
body {
  background: #fcfcfd;
  color: #19181b;
}

.button {
  background: #543a92;
  color: white;
}

.dark-mode {
  color-scheme: dark; // 指示浏览器内置组件使用深色调
  backgound: #19181b;
  color: #e5e4e7;

  .button {
    background: #c3b6e2;
    color: #2a1d49;
  }
}

这样只要在 <body> 上添加 .dark-mode 样式就可以切换到深色主题:

<body class="dark-mode">
  ...
</body>

上面代码可以工作,但如果需要适配的组件有很多时会变得很复杂。我们需要一种方法抽颜色主题的公共部分,而不是一个个组件适配。

CSS 变量

深色主题要修改的主要是各类颜色属性,我们可以把颜色属性抽取成 CSS 变量

用 CSS 变量重构前面的 CSS:

:root {
  --color-background: #fcfcfd;
  --color-on-background: #19181b;
  --color-primary: #543a92;
  --color-on-primary: white;
}

.dark-mode {
  color-scheme: dark;
  --color-background: #19181b;
  --color-on-background: #e5e4e7;
  --color-primary: #c3b6e2;
  --color-on-primary: #2a1d49;
}

body {
  background: var(--color-background);
  color: var(--color-on-background);
}

.button {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

这样跟深色主题相关的内容都被集中到一个地方,修改组件样式的时候就不用考虑主题颜色。

Media Query

前面的样式使用了 .dark-mode 作为是否应用深色主题的分界点,能不能自动适配用户设备的颜色偏好呢?答案是肯定的,现在主流浏览器已经支持了 prefers-color-scheme 这个媒体属性。

使用 prefers-color-scheme 重构前面的代码:

:root {
  --color-background: #fcfcfd;
  --color-on-background: #19181b;
  --color-primary: #543a92;
  --color-on-primary: white;
}

// mixin 是 SCSS 功能,用于抽取重复代码
@mixin dark-scheme {
  --color-background: #19181b;
  --color-on-background: #e5e4e7;
  --color-primary: #c3b6e2;
  --color-on-primary: #2a1d49;
}

@media (prefers-color-scheme: dark) {
  :root {
    @include dark-scheme;
  }
}

.dark-mode {
  color-scheme: dark;
  @include dark-scheme;
}

body {
  background: var(--color-background);
  color: var(--color-on-background);
}

.button {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

这样就可以实现默认情况下根据用户设备预设使用深色主题,在用户需要自定义的时候也可以通过 className 覆盖默认设置。

总结

以上就是通过 CSS 变量和 Media Query 实现深色主题的方法。现在越来越多网站支持深色主题以满足不同的用户环境需求,如果你需要实现深色主题,不妨考虑用本文的方法管理 CSS。

5