bem 命名需语义化、独立且可复用,块名全小写短横线分隔,元素仅直属于块,修饰符仅作用于块或元素本身,禁用深层嵌套与后代选择器。

块名必须语义化且独立存在
块不是任意父容器,也不是 HTML 标签名。它得是一个功能完整、可复用、脱离上下文仍能理解的单元,比如 .search-form、.user-card,而不是 .section-2 或 .wrapper。
常见错误是把通用容器当块:用 .container 或 .row 命名,结果样式强耦合布局结构,一改布局就得全盘重写 CSS。
- 块名全小写,单词间用短横线分隔(
.nav-menu✅,.NavMenu❌) - 不带状态或位置描述(
.top-banner❌,应为.banner+.banner--top) - 不嵌套命名(
.card-header❌,这是元素,不是新块;正确是.card__header)
元素名只用于直接子级,且不可脱离块存在
__ 连接的是块与其**直系组成部分**,不是任意嵌套层级。判断标准很实在:删掉块,这个元素还有意义吗?没意义,就该是元素;有意义,它自己就是个块。
例如 .profile-card__avatar 合理,因为头像离开用户卡片通常不单独复用;但 .logo 如果在页头、页脚、弹窗里都用同一套样式,那它就该是独立块 .logo,而不是 .header__logo。
- 元素名不加前缀(
.card__title__link❌,BEM 不支持三层嵌套) - 避免过深路径(
.form__field__input__icon是反模式,应拆成.form-field块 +.form-field__input+.form-field__icon) - 元素不能单独使用——HTML 中必须同时出现块和元素类:
<div class="card"> <h3 class="card__title">...,而非只写 <code>class="card__title"修饰符只作用于块或元素本身,不传递、不组合错层
--修饰符永远挂在块或元素上,不能跨级修饰,也不能“修饰修饰符”。比如.button--primary__text是非法写法——__text是元素,--primary是块级修饰符,二者不能混搭。真正有效的组合只有两种:
.button--disabled(块修饰符)、.button__label--hidden(元素修饰符)。多个修饰符可共存:.button--primary--large,但前提是它们逻辑正交(比如尺寸和颜色不冲突)。- 修饰符名建议用布尔型(
--disabled、--active)或键值型(--size-small),避免模糊词如--blue(颜色应由主题系统或 CSS 变量控制) - 不要用修饰符替代 JS 状态管理:
--hidden应配合 JS 切换类,而不是单纯靠 CSS 隐藏所有内容 - 修饰符不改变 DOM 结构,只改样式。如果某个变体需要额外标签或属性,说明它可能已超出 BEM 范畴,该考虑封装新块
避免选择器过长和过度嵌套
BEM 的初衷是提升可维护性,但写成
.page-layout__main-content__article__header__title__link就完全违背了本意——不仅难读,还会拖慢浏览器 CSS 匹配性能(CSS 引擎从右往左匹配,长选择器触发大量回溯)。实际项目中,超过两层(
block__element)就该警惕。如果真需要更细粒度控制,优先考虑拆出新块,而不是加深元素层级。- 推荐最大深度:块 → 元素(
.card__body),最多再加一层修饰符(.card__body--compact) - 不用后代选择器替代 BEM 类名:
.card .title❌,必须显式写出.card__title - 禁止用标签名或 ID 做 BEM 选择器基础:
div.card或#main-card会破坏 BEM 的语义唯一性和可移植性
- 修饰符名建议用布尔型(











