card__body__list__item是非法写法,因bem规定element必须直属于block,而card__body仅为card的element,无权作为父级承载list__item;正确做法是将list提级为独立block并用modifier(如list--in-card)表达上下文差异。

为什么 card__body__list__item 是非法写法
它违反 BEM 最基本的结构约束:Element(元素)只能直属于 Block(块),不能嵌套 Element。浏览器和构建工具不会报错,但这个类名已失去语义控制力——card__body 本身只是 card 的一个 Element,没有资格充当父级 Block 角色。
常见错误现象包括:form__field__input__icon、dashboard__content__table__row,它们看似“顺延 DOM 层级”,实则把组件职责全压在命名上,导致:
- 挪动 DOM 结构(比如把 list 拿到弹窗里)时,类名静默失效
- 无法独立复用
list__item,因为它被绑死在card__body路径下 - SCSS 中写
&__body { &__list { } }会编译出非法类名,且易误加 padding 破坏 Element 纯展示语义
怎样把深层嵌套转成合法 BEM
核心动作只有一个:把中间层“提级”为 Block,不是改名字,是重构职责边界。
原结构:<div class="card"><div class="card__body"><ul class="card__body__list"><li class="card__body__list__item">
<p>正确做法:</p>
<ul>
<li>删掉所有 <code>__body__list 类名,把 list 升格为独立 Block:<ul class="list"><li class="list__item"></ul>
list--in-card,而不是靠父级类名推导<div class="card__body">
<ul class="list list--in-card">
<li>CSS 中只写 <code>.list、.list__item、.list--in-card,禁止 .card__body .list 这类后代选择器
button--in-card 和 card__body__button 的本质区别
前者是 Modifier,后者是非法嵌套。关键判断标准是:这个变体是否改变组件本质?
button--in-card 合理,因为按钮仍是按钮,只是尺寸/边距等外观因上下文微调;而 card__body__button 暗示按钮脱离了 button 的语义体系,变成卡片私有产物。
Modifier 必须满足:
- 能独立生效:
.button--in-card加在任何地方都应有预期效果,不依赖.card存在 - 聚焦状态或行为:
--disabled、--loading✅,--inside-header❌(位置词不可预测) - 不传递、不组合错层:
.button__label--hidden✅,.button--primary__label❌(修饰符不能跨级挂到元素上)
真正容易被忽略的不是“怎么起名”,而是“要不要拆”
每次写新类名前,必须问一句:这个 UI 片段,下次出现在另一个页面时,我愿不愿意直接复制粘贴这段 HTML 和 class?如果犹豫了,大概率它不该是 Element,而该是一个 Block。
比如 logo 出现在页头、页脚、弹窗里都用同一套样式,那它就是独立 Block .logo,不是 .header__logo;price-tag 能说清自己是价格标签,就不需要 product-card__price-tag 来背书。
工具链能守住底线:postcss-bem-linter 会报错 .card-header-title(缺 __),eslint-plugin-css-modules 能防住手写 className="card card-header" 这种非 BEM 写法。但工具不替代判断——敢把一个“看起来很像子元素”的东西单独拎出来当成 Block 维护,才是 BEM 落地最难也最关键的一步。











