方案样例 · 示意数据 · 由 码上交付 制作 返回案例 我也要做一个 →
阿澈的技术笔记

你好,我是阿澈(虚构人物)

全栈开发者,写 Node.js 和前端,也折腾效率工具。这里记录踩过的坑、做过的小项目和一些随想。

近期文章

作品集

业余时间做的小东西

记账小助手

命令行 + Web 双端的个人记账工具,CSV 导入、按月统计。

Node.js · SQLite · Vue

番茄钟小程序

极简番茄钟,支持白噪音和每日专注统计。

小程序 · TypeScript

Markdown 转海报 CLI

把一段 Markdown 渲染成适合发朋友圈的长图。

Node.js · Canvas

稍后读浏览器插件

一键收藏网页到看板,自动提取正文与阅读时长。

Chrome Extension · IndexedDB
返回文章列表

用 CSS 变量实现暗色模式:三种方案与取舍

约 6 分钟读完前端CSS

给博客加暗色模式,是很多人做个人站时的头一个“小需求”。看起来只是换个颜色,但真做起来会遇到几个问题:要不要跟随系统?用户手动切换后怎么记住?刷新时为什么会闪一下白?这篇笔记把我试过的三种方案整理一下。

本文为演示内容,用于展示博客的排版、代码高亮与暗色模式效果。右上角的按钮就是本文方案二 + 方案三的实际效果。

方案一:只跟随系统

省事的做法是把颜色都定义成 CSS 变量,然后在 prefers-color-scheme 媒体查询里重新赋值。不需要一行 JavaScript:

theme.css
/* 亮色为默认值 */
:root {
  --bg: #FAFAF7;
  --ink: #1B2130;
  --radius: 14px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0E1117;
    --ink: #E5E9F0;
  }
}

body { background: var(--bg); color: var(--ink); }

缺点也很明显:用户没法单独给这个网站选择亮色或暗色。

方案二:手动切换 + 记住选择

把主题挂到 <html data-theme="dark"> 上,CSS 用属性选择器覆盖变量;切换时写入 localStorage。注意存储在隐私模式下可能抛异常,要包一层 try/catch:

theme.js
const KEY = 'theme';

// 读取用户上次的选择,没有就跟随系统
function getTheme() {
  let saved = null;
  try { saved = localStorage.getItem(KEY); } catch (e) {}
  if (saved === 'light' || saved === 'dark') return saved;
  return matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

function toggleTheme() {
  const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
  document.documentElement.dataset.theme = next;
  try { localStorage.setItem(KEY, next); } catch (e) {}
  return next;
}
当前主题:light

方案三:避免首屏闪烁

如果切换逻辑写在页面底部的脚本里,暗色用户刷新时会先看到一帧白色背景。解决办法是把读取主题的几行代码内联到 <head> 里、在 CSS 之前执行:

index.html
<script>
  // 阻塞执行,但只有几行,几乎没有性能影响
  (function () {
    var t = null;
    try { t = localStorage.getItem('theme'); } catch (e) {}
    if (t !== 'light' && t !== 'dark') {
      t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    }
    document.documentElement.setAttribute('data-theme', t);
  })();
</script>

另外,图片和图表也要考虑暗色:SVG 图标用 currentColor,截图类图片可以加一点 filter: brightness(.9)。

小结

  • 纯展示站:方案一足够,零 JS。
  • 博客 / 文档站:方案二 + 方案三,体验更完整。
  • 颜色一律走变量,后期换主题色只改一处。

完整示例就是你现在看到的这个页面,可以打开开发者工具看看源码。

阿澈(虚构人物)
全栈开发者。这是一个博客演示样例,文章内容仅用于展示排版效果。