data-theme 必须挂载在 html 或最外层容器上以生效,BEM 修饰符仅是语义钩子;data-role 用于结构识别,不可替代 BEM Block;button--disabled 与 data-state="disabled" 语义分离,禁止混用。

data-theme 必须挂载在根节点或容器上,不能只靠 class 切换
仅给按钮加 class="button--dark" 是无效的——BEM 修饰符本身不带样式逻辑,它只是个语义钩子。真正起作用的是 CSS 中用 html[data-theme="dark"] .button--primary 这类更具体的选择器去重定义 --theme-color 变量。
常见错误现象:document.body.classList.add("dark") 后样式没变,因为没配 data-theme 属性,stylelint 也拦不住这种运行时问题。
-
data-theme应设在或最外层组件容器(如<div data-theme="dark">),确保作用域覆盖所有子组件 <li>必须和 BEM 修饰符语义对齐:<code>data-theme="dark"对应button--dark,不能混用data-theme="dark"+button--light - 避免用 JS 直接操作
style属性覆盖变量,会破坏 CSS 变量链:不要写el.style.setProperty("--theme-color", "#111")
data-role 比元素类名更适合控制内部结构样式
当需要根据 DOM 结构动态调整子元素(比如图标颜色随主题变化),别写 .button__icon { color: var(--theme-color); } 然后靠 HTML 里是否套了 button__icon 类来判断——这会让样式强耦合于类名存在与否,一旦组件重构就断。
正确做法是用 data-role 做轻量级语义标记,再配合属性选择器:
立即学习“前端免费学习笔记(深入)”;
<button class="button"> <span data-role="icon"></span> <span data-role="text">提交</span> </button>
对应 CSS 写成:
.button [data-role="icon"] {
color: var(--theme-color);
}
-
data-role不参与 BEM 命名,不被 PurgeCSS 扫描,也不受stylelint-selector-bem-pattern约束,纯粹用于结构识别 - 比
button__icon更灵活:同一个data-role="icon"可出现在card、nav-item等不同块中,无需重复定义元素类 - 禁止用
data-role替代 Block:不能只有<div data-role="button"> 而不加 <code>class="button",否则丧失 BEM 的可复用契约BEM 修饰符与 data-state 不要混用
button--disabled和data-state="disabled"解决的是不同层面的问题:前者声明组件视觉变体,后者描述运行时状态。强行合并会导致语义坍塌。典型翻车点:
<button class="button button--disabled" data-state="disabled"></button>—— 两个字段表达同一事实,但更新不同步(JS 改了data-state却忘了切 class)就会样式错乱。- 优先用 BEM 修饰符控制外观:
button--disabled负责灰掉、禁用指针、降低透明度 - 用
data-state辅助可访问性或调试:data-state="loading"可配合 ARIA 属性,或供 Cypress 测试定位 - 禁止在 CSS 中写
[data-state="disabled"]替代.button--disabled,否则破坏 BEM 的静态分析能力(clsx类型推导、PurgeCSS 识别都会失效)
SCSS 中禁止用 data-attribute 嵌套生成 BEM 类名
有人想偷懒,在 SCSS 里写:
.button { &[data-theme="dark"] { --theme-color: #1a1a1a; } }看起来没问题,但实际编译出的是
.button[data-theme="dark"],这个选择器权重低于html[data-theme="dark"] .button--primary,变量不会被重写。- 所有主题变量覆盖规则,必须以
html[data-theme]或容器[data-theme]开头,确保 specificity 足够高 - SCSS 嵌套只允许用
&__和&--,任何带空格或属性选择器的嵌套(如&[data-theme])都违反 BEM 隔离原则 - 如果组件需局部换肤(比如卡片内按钮走独立主题),用
card[data-theme="light"] .button--primary,而不是把data-theme下放到 button 元素上
最容易被忽略的是:
data-属性不是 BEM 的替代品,而是它的补位工具——它管结构与状态流转,BEM 管命名与样式归属。两者边界一旦模糊,比如用data-element="title"替代card__title,整个组件的可维护性就退回到 pre-BEM 时代。 - 优先用 BEM 修饰符控制外观:


















