card__header__title是非法类名,因bem仅允许block__element和block--modifier两种结构,三重下划线违背语义独立原则,导致复用困难、维护脆弱。

直接禁止 block__element__element 这种写法,它不是浏览器报错的问题,而是语义崩坏的起点——BEM 里根本没有“孙子元素”这个概念。
为什么 card__header__title 是非法类名
BEM 规范只允许两种结构:block__element 和 block--modifier。出现第三个 __(比如 card__header__title)就等于在声明“header 是 block,title 是它的 element”,但 card__header 本身只是 card 的一个 element,不能充当 block 角色。
- 浏览器不会拒绝这个类名,但团队协作时没人敢动它:改
card__header结构?card__header__title就失效 - 无法单独测试或抽离:你没法把
card__header__title拿到别的页面复用,它被 DOM 路径锁死了 - SCSS 里写
&__header { &__title { ... } }会编译出非法类名,还容易误给card__header加 padding,破坏 element 的纯展示语义
DOM 嵌套深,HTML 怎么写才不破 BEM
HTML 可以保持三层甚至四层嵌套,关键在类名归属——不要让类名跟着 DOM 层级走,而要按语义角色分块。
- 如果
title在多个地方复用(如用户卡片、订单摘要、弹窗头部),就定义为独立 Block:heading或section-title - 如果它只出现在
card__header里且样式强依赖上下文,优先用 Modifier:heading--in-card-header - 微调布局(比如上下间距、颜色)靠父级类名控制:
.card__header .heading { margin-bottom: 8px; }—— 这种嵌套仅限一层,且只用于布局微调,不定义核心样式 - 避免用
card__body__button-group这类命名:它搜不到、切不出、button-group 失去语义独立性
真需要表达“视觉父子关系”时怎么处理
极少数场景下,比如折叠面板里的二级标题、弹窗 footer 中的按钮组,它们几乎从不在别处复用,且样式高度依赖容器。这时不该造新 Block,而该用更轻量、更可控的方式。
- 加 State 类:
class="heading is-subheading",再写.is-subheading { font-size: 0.9em; } - 用
data-属性标记上下文:<div data-context="accordion"> <h3 class="heading">,对应 CSS:<code>[data-context="accordion"] .heading { margin-top: 4px; } - React/Vue 中可通过 props 注入 context 类名,避免硬编码
card__header__title - 绝对不要为了“看起来像嵌套”而牺牲语义独立性——BEM 的 block 必须能独立存在,这是底线
最容易被忽略的一点是:BEM 不是字符串拼接游戏,card__header__title 看似顺理成章,实则把 DOM 实现细节暴露给了 CSS,一旦结构微调,所有依赖它的样式都会悄无声息地失效。











