bem 禁用 card__header__title 是因它违反语义边界:card__header 仅为 element,非独立 block;多级下划线导致复用困难、样式失效、scss 编译错误,并破坏 element 纯展示职责。

card__header__title 这种写法为什么必须禁用
它不是语法错误,而是语义断裂的信号。BEM 只允许 block__element 和 block--modifier 两种结构,出现第三个 __(如 card__header__title)等于把 card__header 当作新 block 使用——但 card__header 本身只是 card 的一个 element,没有独立职责边界。
后果很直接:card__header__title 无法抽离复用;改 card__header 的 DOM 结构(比如换成 card__section)时,样式静默失效;SCSS 中写 &__header { &__title { } } 会编译出非法类名,还容易误给 card__header 加 padding,破坏 element 的纯展示语义。
深层 DOM 嵌套时,Element 该挂在哪一级
关键不是看 HTML 嵌套了几层,而是问:这个节点是否构成一个可复用、有明确功能边界的 UI 单元?如果不是,它就不该有自己的 BEM 类名。
- 反模式:
<div class="card"><div class="card__body"><div class="card__body__list"><div class="card__body__list__item">...</div></div></div></div> - 正解:把
list抽成独立 block ——list、list__item、list__empty,然后在card__body里放一个<div class="list"> <li>如果只是卡片内一段固定文案,且绝不出现在别处,用 modifier 更合适:<code>card__body-text--subtle或状态类is-in-card-body
多级导航菜单中 dropdown 算不算 card 的 element
不算。dropdown 是可独立显隐、有自身过渡逻辑、可能在多个上下文复用的交互单元,它和 card(或 nav)职责不重叠。强行写成 card__dropdown 或 nav__item__dropdown,等于把两个不同生命周期、不同维护主体的模块耦合在一起。
正确做法是让 dropdown 自成 block:dropdown、dropdown__toggle、dropdown__menu,然后在 card__actions 或 nav__item 内直接嵌入实例:
<div class="card">
<div class="card__content">...</div>
<div class="card__actions">
<button class="button">编辑</button>
<div class="dropdown">
<button class="dropdown__toggle">⋯</button>
<div class="dropdown__menu">...</div>
</div>
</div>
</div>
这样改 dropdown 动效不会影响 card 布局,复用到弹窗或表格操作列也完全无压力。
工具链怎么防住手写 BEM 的常见翻车点
靠人盯代码三天就放弃。真正落地得靠约束前置:
- VS Code 安装
BEM Tools插件,输入btn__text--hover按 Tab 自动补全完整结构 - Sass/PostCSS 用
postcss-bem,写.button { &__text { &--hover {} } }编译后自动转成标准类名,不手拼 - React 项目用
clsx封装函数,如cn('button', { 'button--disabled': disabled }),避免漏掉--或大小写错位 - 绝对禁用 Sass 的
@extend模拟 BEM 关系——它会合并选择器,破坏“单一样式来源”原则
最常被忽略的一点:Modifier 必须表达有限、可枚举的状态变体,而不是补层级缺口。card__header__title--small 是错的,heading--in-card-header 才对——因为后者是明确变体,且可在多个容器中复用。











