不能用 body--dark 因为它违反 bem 契约、无法响应系统偏好、易被 purgecss 删除、破坏原生控件适配;应改用 html[data-theme="dark"] 统一管理主题。

为什么不能用 body--dark 这类写法
因为 body 不是你定义的 Block,body--dark 直接违反 BEM 命名契约。更实际的问题是:它无法响应系统偏好变化 —— window.matchMedia('(prefers-color-scheme: dark)') 触发时,这个 class 不会自动增删,必须手动监听+同步,漏一处就失焦;PurgeCSS 构建阶段大概率把没在首屏 HTML 中出现的 body--dark 样式全删掉;原生 <select></select> 等控件在深色系统下本应自动适配,但被 body--dark input 强制覆盖后反而不可读。
应该用 html[data-theme="dark"] 而不是 app--theme-dark
虽然有些资料提过 app--theme-dark,但权威实践已明确:主题控制权必须落在 html 元素上,且必须用 data-theme 属性。原因很直接:data-theme 可被 CSS 自定义属性天然捕获,支持媒体查询 fallback(如 @media (prefers-color-scheme: dark) { html { data-theme: 'dark'; } }),而 class 修饰符无法触发变量重计算。BEM 修饰符(如 .button--primary)只表达组件角色,不承担主题上下文职责。
.button--primary 怎么配合深色模式生效
基础类只声明变量引用,不写死值:
.button--primary {
background-color: var(--bg-primary, #007bff);
color: var(--text-on-primary, white);
}
主题覆盖必须用更具体选择器,确保变量重定义生效:
html[data-theme="dark"] .button--primary {
--bg-primary: #1e1e1e;
--text-on-primary: #fff;
}
- 所有颜色/背景等外观属性统一走
var(--xxx),并带合理 fallback - 不要写
.button--primary--dark—— 这是语义错位,也导致 SSR 白屏、PurgeCSS 误删 - 若组件嵌套深(如
.card__header .button--primary),主题规则也要保持同等深度,或改用:where()降权避免权重失控
JS 切换时只改 document.documentElement.dataset.theme
切换逻辑必须极简:
document.documentElement.dataset.theme = 'dark';
// 不要操作 body.className、不要加 class 到 app 容器、不要写 document.body.classList.toggle('body--dark')
关键点在于:变量作用域由 HTML 属性决定,而非 DOM 类名存在与否。这意味着:
- SSR 渲染可直接输出
,无需等待 JS - 媒体查询可兜底:
@media (prefers-color-scheme: dark) { html:not([data-theme]) { data-theme: 'dark'; } }(注意:部分浏览器需 JS 补充) - 第三方库(如 Bootstrap)若依赖
data-theme,必须确认其 update 方法是否被调用 —— 仅设属性不会触发变量重计算
最易被忽略的是 fallback 和媒体查询顺序:变量声明必须放在 :root 默认定义之后,否则深色规则会被浅色值覆盖;所有 var(--x, #fallback) 的 fallback 值都要经过设计验证,不能只写 #000 应付了事。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











