主题切换与无闪烁加载
持久化用户主题选择、避免 FOUC、与系统偏好同步。
设计令牌进阶
为什么
重载时闪白主题切换是坏 UX。内联阻塞 CSS + 水合前脚本在首绘前设主题,用户永不见错主题。
Tokens Studio(原 Figma Tokens)有 50 万以上安装量,W3C 设计令牌格式模块即将成为候选推荐。令牌从单一来源实现暗色模式、多品牌主题和平台一致性。
何时使用
加暗色模式那一刻就实现无闪烁加载。后补意味着每次重载闪到修好 — 第一次就做对。
如何做
1. <head> 内联脚本 — body 渲染前运行,读 localStorage,设 data-theme
2. 每主题 CSS 变量 — :root[data-theme="dark"] { --color-bg: ... }
3. 持久选择 — localStorage "theme" = "light"|"dark"|"system"
4. 系统同步 — matchMedia("(prefers-color-scheme: dark)") 监听器在 "system" 时更新
5. 切换组件 — 无障碍 switch、aria-label、更新 attr + storage
6. SSR — 服务器读 cookie(非 localStorage)设初始 data-theme,登录用户无闪
7. 过渡 — color-scheme: light dark,加 bg/color 上 150ms 过渡
示例
<!-- index.html — <head> 内联脚本,阻塞首绘 -->
<head>
<script>
(function () {
var t = localStorage.getItem("theme") || "system";
var dark = t === "dark" || (t === "system" && matchMedia("(prefers-color-scheme: dark)").matches);
document.documentElement.dataset.theme = dark ? "dark" : "light";
})();
</script>
</head>
/* tokens.css — 每主题变量集 */
:root[data-theme="light"] { --color-bg: #ffffff; --color-text: #111827; }
:root[data-theme="dark"] { --color-bg: #0f172a; --color-text: #f3f4f6; }
body { background: var(--color-bg); color: var(--color-text); transition: background 150ms, color 150ms; }
// ThemeToggle.tsx — 无障碍 switch
function ThemeToggle() {
const [theme, setTheme] = useState(localStorage.getItem("theme") || "system");
const toggle = () => {
const next = theme === "dark" ? "light" : "dark";
setTheme(next);
localStorage.setItem("theme", next);
document.documentElement.dataset.theme = next;
};
return <button onClick={toggle} aria-label="切换暗色模式">{theme === "dark" ? "☀" : "☾"}</button>;
}
常见问题
- ⚠重载 FOUC — 水合后应用主题,用户见白闪后变暗
- ⚠仅 localStorage — SSR 用户首绘错主题,无 cookie 回退
- ⚠无系统同步 — 用户 OS 暗模式被忽略,每应用须手动切
最佳实践
- ✓<head> CSS 前内联主题脚本 — 阻塞首绘,零闪
- ✓默认提供 'system' — 尊重 OS 设置,显式选择者覆盖之
- ✓SSR 用 cookie — 服务器读并在 HTML 设 data-theme,登录用户无闪