用CSS变量做主题切换三种写法各有各的坑
【摘要】 用 CSS 变量做主题切换,三种写法各有各的坑给一个小工具加暗色模式,本来以为十分钟的事,结果前后改了三种写法。记录一下各自的问题,给要踩坑的人省点时间。写法一:JS 直接改内联样式最直觉的做法,元素上直接写:document.body.style.background = '#111';document.body.style.color = '#eee';问题很快显现:要改的地方越来越多,...
用 CSS 变量做主题切换,三种写法各有各的坑
给一个小工具加暗色模式,本来以为十分钟的事,结果前后改了三种写法。记录一下各自的问题,给要踩坑的人省点时间。
写法一:JS 直接改内联样式
最直觉的做法,元素上直接写:
document.body.style.background = '#111';
document.body.style.color = '#eee';
问题很快显现:要改的地方越来越多,背景、文字、边框、按钮、输入框……每个元素都得单独处理,代码里到处都是颜色字符串。
这是最容易想到、也最难维护的写法。适合一次性改一两个属性,不适合主题系统。
写法二:切换 class,配色写在 CSS 里
body.dark { background: #111; color: #eee; }
body.dark .card { background: #1c1c1c; }
好处是配色集中在样式表,JS 只切一个 class:
document.body.classList.toggle('dark');
问题是重复太多。每个用到颜色的选择器都要写两遍,漏一个就会出现"暗色主题下某块还是白的"。
这个写法用了挺久,能跑,但每次改设计都要全表搜一遍颜色值。
写法三:CSS 变量 + 单点切换(最终方案)
定义一套变量,所有颜色只引用变量名:
:root {
--bg: #ffffff;
--fg: #191919;
--border: rgba(0,0,0,.08);
--accent: #c7000b;
}
[data-theme="dark"] {
--bg: #141414;
--fg: #e8e8e8;
--border: rgba(255,255,255,.12);
--accent: #ff6b6b;
}
body { background: var(--bg); color: var(--fg); }
.card { border: 1px solid var(--border); }
切换只改一个属性:
document.documentElement.dataset.theme = 'dark';
好处很直观:
- 颜色定义只有一处,改主题不用翻样式表
- 组件不需要知道当前是什么主题,只引用变量
- 加第三套、第四套主题,只是多一段变量块
三个实际踩到的细节
1. 变量要挂在 :root 而不是 body
挂在 body 上时,html 的背景色不会跟着变,滚动区域露白边。挂在 :root(即 html)上才彻底。
2. 切换时要加过渡,但别加在所有元素上
body { transition: background .2s, color .2s; }
如果写成 * { transition: all .2s },页面上的动画、hover 效果会全部被拖慢,感觉整个页面变"黏"。
3. 记住用户选择
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.dataset.theme = saved;
但要注意:如果在 <head> 里异步读取,页面会先按亮色渲染一帧再跳成暗色,闪一下白光。解决办法是把这段脚本内联在 <head> 里同步执行,别等 DOM 加载完。
小结
三种写法不是"对错"关系,是适用场景不同:
| 写法 | 适用 |
|---|---|
| 内联样式 | 临时改一两个属性 |
| class 切换 | 只有两套主题、组件数量少 |
| CSS 变量 | 组件多、主题可能扩展、需要长期维护 |
判断标准其实很简单:如果你预感到将来会有第三套主题,一开始就用变量。否则早晚要重构一遍。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)