BEM规范下CSS类名逻辑结构为Block-Element-Modifier三段式:Block是独立可复用的功能单元(如.card),Element是其直接子部件(如.card__title),Modifier表达状态或变体(如.button--disabled)。

直接改类名不等于转成BEM——很多团队把 .header .nav li a 改成 .header-nav-link,这反而破坏了BEM语义,也埋下维护隐患。
识别原始结构中的 Block 边界
BEM 的起点不是“怎么改名”,而是“哪些东西该被当作独立 Block”。一个 Block 必须满足:能脱离上下文复用、有明确语义、样式不依赖父容器。常见误判是把 .sidebar .widget 当作一个 Block,其实 .widget 才是 Block,.sidebar 只是布局容器,不该参与命名。
- 检查 HTML 中是否用
class显式标记了功能单元(如search-form、user-card),这些就是天然 Block - 忽略仅用于布局的 class(如
col-6、flex-center),它们不属于 BEM 范畴 - 避免把语义模糊的 class(如
.box、.section)当 Block,替换成.product-list或.faq-accordion这类可读性强的名称
重写选择器时禁止嵌套后代写法
BEM 要求所有样式规则必须基于单个 class,不能出现空格或层级关系。写成 .card .card__title 是典型错误,浏览器会按后代选择器解析,权重升高,且违背“扁平化”原则。
- 原始 CSS 中的
.modal .close-btn→ 改为.modal__close(Close 是 Modal 的元素,不是独立按钮) -
button.primary→ 拆成.button(Block) +.button--primary(Modifier),不能保留.primary这种泛义 class - 遇到
ul li a这类无 class 的结构,必须补上 Block 和 Element 类名,例如<nav class="main-nav"><ul class="main-nav__list"><li class="main-nav__item"><a class="main-nav__link">Home</a></li></ul></nav>
Modifier 命名要区分状态与外观
修饰符不是随便加后缀,--disabled 和 --large 本质不同:--disabled 描述交互状态(state),应由 JS 控制增删;--large 描述外观变体(theme),应在模板中静态声明。
立即学习“前端免费学习笔记(深入)”;
- 状态类(如
--loading、--error)必须和对应 Block/Element 同时存在,不能单独使用,例如<button class="button button--loading"> - 外观类(如
--outline、--inverted)可组合使用,但需确保 CSS 中无冲突定义,例如.button--outline.button--inverted应有明确样式覆盖逻辑 - 避免用
--hover、--focus这类伪类当 Modifier,它们应通过:hover等伪选择器实现,而非 class 控制
SCSS 中生成 BEM 类名的陷阱
在 SCSS 里写 .card { &__title { } } 看似简洁,实际编译出的是 .card .card__title(带空格),根本不是 BEM 要的单类名。
- 唯一安全写法是
@at-root #{&}__title { },它强制提级并拼接纯块名 - 如果块名来自变量(如
$block: "form"),必须写@at-root #{$block}__input { },不能用&__input - 禁止在
&:hover或@media内部再用@at-root #{&}__xxx,会导致输出顺序错乱,且语义失效 - 批量生成修饰符时,
@each $mod in ("small", "full-width")中的字符串必须带引号,否则 SCSS 报Undefined variable "$small"
真正难的不是命名规则本身,而是每次加一个 class 前,得想清楚:它是 Block?还是 Block 的一部分?它会不会在别处复用?有没有可能被其他组件意外覆盖?这些判断比符号拼写重要得多。


















