<p>BEM 中状态必须用 -- 修饰符,不可嵌入元素名;-- 只能依附 Block 或 Element,表达业务状态而非样式细节,互斥状态需单选,叠加状态可共存。</p>

状态必须用 -- 修饰符,不能塞进元素名里
常见错误是把状态当元素命名,比如写 btn__icon-loading 或 card__title-error。这直接破坏 BEM 层级语义:__ 只表达“从属结构”,不表达“状态变化”。loading 不是图标的新类型,而是图标当前所处的状态——它得用 btn__icon--loading 表达。
真实 DOM 中,状态类必须作用在对应 Block 或 Element 上,且只加在那个元素本身:
<button class="btn btn--loading"> <span class="btn__icon btn__icon--loading"></span> 提交 </button>
-
btn--loading控制按钮整体禁用、背景灰度、指针禁用 -
btn__icon--loading只控制 SVG 动画或路径切换,不碰按钮行为 - 两者可共存,但不可互相替代:只加
btn__icon--loading而不加btn--loading,按钮仍可点击,语义断裂
-- 修饰符只能依附 Block 或 Element,不能跨级或嵌套
修饰符不是独立存在,它必须明确绑定到某个具体 Block 或 Element。禁止写 btn--loading__icon(Modifier 跳到子元素)、btn__icon--loading--spin(双修饰符嵌套)、或 btn__icon--disabled(图标的禁用应由按钮整体状态决定)。
合法写法只有两类:
立即学习“前端免费学习笔记(深入)”;
- Block 级状态:
btn--loading、card--error、dash-chart--loading - Element 级状态:
btn__icon--loading、card__title--highlighted、dash-chart__axis-label--hidden
注意:dash-chart__axis-label--hidden 合法,因为 --hidden 是对这个标签元素自身的显隐控制;但 dash-chart--hidden 才表示整个图表被隐藏——目标不同,不能混用。
避免视觉值修饰符,比如 --red、--12px
修饰符必须表达业务状态或角色,而不是样式细节。写 header__title--red 看似快,实则埋雷:
- 换暗色主题时要全局搜
--red替换,而非只改颜色变量 - 和设计系统 token(如
--color-primary)冲突,无法复用 - JS 判断状态时,
el.classList.contains('header__title--active')比el.classList.contains('header__title--red')更可靠
正确做法是用语义化状态名:btn--primary、card--featured、form-field--invalid。颜色、尺寸等实现细节交给 CSS 自定义属性或设计系统统一管理。
多个互斥状态用组合类,不拼接修饰符
BEM 不允许 btn--primary--disabled 这种写法。互斥状态(如按钮的 primary/secondary/danger)必须单选,而叠加状态(如 btn--large btn--disabled)可以共存。
关键判断点:
- 是否逻辑上可同时成立?
large和disabled可以,就写两个 class - 是否属于同一维度?
primary和secondary是互斥变体,只能选一个 - JS 控制时,用
classList.replace()或classList.toggle()显式切换,别靠字符串拼接
容易被忽略的是:Modifier 的作用目标必须与 HTML 结构严格一致。哪怕只漏加一个 btn--loading,JS 交互和 CSS 样式就可能脱节——BEM 的稳定性,全靠这一层“命名即契约”的约束力。


















