用 CSS 变量实现无闪烁的深色模式
深色模式做不好通常有两个症状:刷新时闪一下白屏,以及某个组件在暗色下依然刺眼。前者是时序问题,后者是架构问题。
一、首帧之前就把主题定下来#
React 水合发生在首屏绘制之后,所以切换逻辑不能放在组件里。要在 <head> 里同步执行:
const themeScript = `(function(){try{
var t = localStorage.getItem('theme');
if (t !== 'dark' && t !== 'light') {
t = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.dataset.theme = t;
}catch(e){}document.documentElement.dataset.theme=document.documentElement.dataset.theme||'light';})();`;<html lang="zh-CN" suppressHydrationWarning>
<head><script dangerouslySetInnerHTML={{ __html: themeScript }} /></head>suppressHydrationWarning 是必要的:服务端不知道用户选了哪个主题,这个差异是预期的。
二、颜色全部走变量,并且只维护一份#
:root {
--bg: #f6f7fb;
--card: #ffffff;
--text: #1a1f2b;
--muted: #5c6577;
--border: #e3e7f0;
}
html[data-theme='dark'] {
--bg: #0d1117;
--card: #161b22;
--text: #e6e9ef;
--muted: #a7b0bf;
--border: #242c37;
}组件里只写 var(--card)、var(--text),永远不要写死 #fff 或 #000。做到这一点,切换主题就是改一个属性的事。
三、容易漏掉的几类颜色#
| 类型 | 建议做法 |
|---|---|
| 代码块 token 配色 | 也定义成变量,暗色下单独给一组 |
| 半透明遮罩(灯箱) | 用 rgba(8,11,16,.9),两种主题共用即可 |
| 阴影 | 暗色下加深,否则层次消失 |
| 表单控件 | 显式设置 background 与 color,别依赖浏览器默认 |
四、跟随系统 + 手动覆盖#
优先级:用户手动选择 > 系统偏好。切换时写回 localStorage:
document.documentElement.dataset.theme = next;
localStorage.setItem('theme', next);这样用户的选择跨页面、跨会话都保留,而没选过的访客自动跟随系统。
自测清单:切到暗色后逐个检查 —— 表格斑马纹、引用块、标签胶囊、分页按钮、404 页、后台表单。这些是最容易出现"半白半黑"的地方。
付费下载 · ¥99.00
付款后立即开通下载链接(30 天有效、可下载 5 次)。在微信中打开本页可直接支付,在电脑上则显示扫码支付。
微信扫码加客服
评论
正在加载评论…