bem禁止使用div.card等类型选择器,因其破坏样式作用域隔离——bem依赖类名自身表达边界,混入标签名会使样式强耦合dom结构,一旦html结构调整(如换用flexbox),样式立即失效;必须仅用.card、.card__title、.card--loading等纯类名选择器。

直接结论:BEM中写 div.card、button.btn 或 ul.nav-list 这类标签+类组合,等于主动放弃样式归属权——它不再属于某个组件,而属于整个 HTML 规范。
为什么 button.btn 看似合理实则危险
表面看是“语义化”,实际让样式强绑定到 button 标签上。一旦换成 <mybutton></mybutton>(React)、<el-button></el-button>(Element Plus)或无障碍升级为 <div role="button">,所有 <code>button.btn 规则立即失效。
- 第三方组件库内部也用
button,你的button.btn:hover可能覆盖它,也可能被它覆盖 - Cypress 测试里
cy.get('button')匹配全页所有按钮,定位不稳;cy.get('.btn--submit')才精准唯一 - 编译后 CSS 权重变成
0,1,1(标签+类),高于标准 BEM 的0,1,0,后续修饰符如.btn--loading很难覆盖
哪些写法看似安全,其实偷偷用了标签选择器
这些不是“语法错误”,而是隐式引入了结构依赖,破坏 BEM 的自治前提:
-
.card .title:后代选择器本身不带标签,但依赖 DOM 层级,换结构就崩 -
form input[type="text"]:既绑定了form容器,又绑定了input标签和属性,三重耦合 -
svg path:图标路径样式应归属.icon--search,而非 SVG 内部结构 -
article h2:把标题样式锁死在article内,挪到卡片或弹窗里就失效
正确替代方案:用 Modifier 或 wrapper 显式表达语义
真有差异化需求时,别妥协写标签选择器,改用 BEM 原生支持的机制:
- 需要区分「提交按钮」和「关闭按钮」?→ 用
.btn--submit和.btn--close,不用button[type="submit"] - 表单内输入框要统一禁用样式?→ 写
.form-field--disabled .form-field__input,而不是.form-field input:disabled - 第三方日期控件嵌入卡片后样式被污染?→ 包一层
<div class="card__content"><datepicker></datepicker></div>,只对.card__content设定 padding/overflow - 老项目过渡期必须限定作用域?→ 用属性选择器兜底:
[data-bem="card"] .card__title,权重仍是0,1,0
工具链怎么守住这道线
光靠人盯容易漏,得靠自动化卡口:
- 启用
stylelint-selector-bem-pattern并开启strict: true模式,CI 直接拦截含空格、>、+或标签名的选择器 - Webpack 构建时加
postcss-bem-linter,配置allowedSelectors: [/^\.[a-z][a-z0-9-]*(__[a-z][a-z0-9-]*)?(--[a-z][a-z0-9-]*)?$/],只放行纯类名 - DevTools 中检查 Styles 面板,生效规则 specificity 必须是
0,1,0;只要出现0,1,1或更高,说明已越界
最常被忽略的一点:BEM 不是“给类名加前缀”,而是让每个类名自己就能回答“我在哪、我是谁、我现在什么状态”。一旦你开始写 div.card,你就已经把判断权交给了 HTML 结构,而不是组件自身。











