错误。body--dark 破坏样式隔离、干扰系统偏好同步、增加 PurgeCSS 误删风险、无法驱动 CSS 变量更新;应改用 html[data-theme="dark"] 统一控制主题变量。

直接用 body 加 BEM 修饰符(比如 body--dark)控制深色模式,是错的。它看起来简单,实则破坏样式隔离、干扰系统偏好同步、增加构建误删风险,且无法可靠驱动 CSS 变量更新。
为什么 body--dark 不该作为主题开关
这不是写法“不优雅”的问题,而是会立刻引发可维护性断裂:
-
body--dark .button--primary这类选择器权重高、嵌套深,后续加.button--primary--large就得再写一遍整条链路 -
window.matchMedia('(prefers-color-scheme: dark)')触发时,body--dark不会自动增删,必须 JS 额外监听+手动同步,漏一处就失焦 - PurgeCSS 默认只保留 HTML 中显式出现的 class;如果用户首次访问是亮色模式,
body--dark从没在 DOM 中出现过,相关样式大概率被干掉 - 原生表单控件(如
<select>)在深色系统下本应自动变暗,但body--dark input强制覆盖背景/文字色,反而让控件不可读或失去系统一致性
正确做法:用 data-theme + BEM 修饰符双重定位
主题控制权必须落在根节点上,BEM 修饰符只负责组件状态语义,两者分工明确:
- HTML 中写
<html data-theme="dark">,而不是<body class="body--dark"> - CSS 中用
html[data-theme="dark"] .button--primary { --bg-color: #1e1e1e; }覆盖变量,不是重写所有属性 - BEM 修饰符(如
.button--primary)保持不变,它只声明“这是主按钮”,不承担“当前主题是什么”的职责 - JS 切换时只改
document.documentElement.dataset.theme = 'dark',不操作body.className
常见错误:把修饰符当主题容器来用
比如写 .app--dark .card--header 或 body.app--dark .btn,本质是混淆了「作用域」和「状态」:
立即学习“前端免费学习笔记(深入)”;
-
.app--dark不是 BEM 合法块名 ——app不是你定义的组件,强行加修饰符违反命名契约 - 这种写法会让所有深色规则依赖一个高权重前缀,导致未来想抽离某个组件的深色样式时,必须连带改几十处选择器
- 若组件同时有状态(如
.btn--disabled)和主题(.btn--dark),你得写.app--dark .btn--disabled和.app--dark .btn--primary两套,变量复用完全失效 - 更稳的做法是:只用
.btn--primary声明变量引用,靠html[data-theme="dark"]统一注入新值,--bg-color一处改,全链路响应
真正容易被忽略的是:BEM 修饰符本身不解决主题切换,它只是提供了一个干净的钩子。关键在于把变量作用域锚定在 html[data-theme] 这一层——否则任何基于 class 的“主题类”都会在 SSR、伪类继承、构建优化中出问题。


















