card__header__title是错的,因BEM仅允许block__element和block--modifier两种结构,三段式命名将card__header误当作Block,违背“元素仅属块直系”原则,导致复用困难、语义断裂、DOM变动时样式静默失效。

card__header__title 为什么是错的
它不是浏览器报错的问题,而是语义崩坏的起点。BEM 只允许 block__element 和 block--modifier 两种结构,card__header__title 出现了第三个 __,等于把 card__header 当作新 Block 使用——但 card__header 本身只是 card 的一个 Element,不能承担 Block 角色。
这种写法会导致:
- 无法单独复用 card__header__title,它被 DOM 路径锁死
- 改动 card__header 结构时,所有依赖它的样式悄无声息失效
- SCSS 中写 &__header { &__title { } } 会编译出非法类名,还容易误给 card__header 加 padding,破坏 Element 的纯展示语义
深层嵌套时该不该抽新 Block
关键判断标准不是“HTML 嵌套了几层”,而是“这个 UI 片段会不会在别处复用?是否拥有自己的逻辑或样式边界?”
例如:card__body__list__item__text 这种超长名,大概率说明 list 本该是独立 Block:
- 如果
list在用户列表、搜索结果、侧边栏都出现,就该定义为独立listBlock - 如果
list__item需要独立交互(如点击展开)、有自己的动画或状态管理,那它更不该依附于card - 如果只是卡片内一段固定文案,且绝不出现在别处,可用 Modifier 或 State 类替代,比如
card__body-text--subtle或class="text is-in-card-body"
Modifier 和 State 类怎么选
Modifier 不是用来补层级缺口的,而是表达有限、可枚举的状态变体;State 类或 data- 属性更适合一次性、弱复用、强上下文依赖的场景。
立即学习“前端免费学习笔记(深入)”;
常见选择依据:
-
heading--in-card-header:适合字体缩小 10%、行高微调等明确变体,且该变体在多个容器中复用 -
is-subheading:适合折叠面板里的二级标题这类一次性场景,CSS 写.is-subheading { font-size: 0.9em; } -
data-context="modal":适合样式高度依赖容器但又不想污染 Block 命名,CSS 写[data-context="modal"] .button { padding: 4px 12px; }
绝对禁止 card__header__title--small:这等于把两层语义塞进一个 Modifier,违反 block--modifier 单一职责原则。
HTML 嵌套深,CSS 怎么保持扁平
HTML 可以保持三层甚至四层嵌套,关键不在结构深度,而在类名归属是否清晰。BEM 不要求 DOM 扁平化,只要求类名不随 DOM 层级“滑动”。
布局微调(如上下间距、颜色)可以靠父级类名控制,但仅限一层嵌套,且只用于微调:
-
.card__header .heading { margin-bottom: 8px; }是可接受的,前提是.heading是独立 Block - 禁止写
.card__header .card__body .button—— 这已超出 BEM 允许的嵌套范围 - 真正需要多层容器的场景(如仪表盘卡片 + 表格 + 分页),每个容器应有独立 Block 名称,而不是让所有子元素都挂在顶层 Block 下
最容易被忽略的一点是:BEM 不是字符串拼接游戏。card__header__title 看似顺理成章,实则把 DOM 实现细节暴露给了 CSS,一旦结构微调,所有依赖它的样式都会悄无声息地失效。


















