disabled状态必须用.btn--disabled,因BEM规范强制修饰符使用--分隔,否则会被误判为独立块或无效类,导致构建误删、DevTools无法识别;且须同步设置disabled属性、pointer-events: none及opacity等视觉反馈,并用classList.toggle控制。

disabled 状态必须用 --disabled 修饰符,不能写成 .btn-disabled
直接写 .btn--disabled 是唯一合规方式。BEM 规范强制要求修饰符(Modifier)必须用双连字符 -- 分隔,否则会被解析为独立 Block 名或普通类名——构建工具可能误删,DevTools 里也识别不出这是状态类。
常见错误包括:.btn-disabled(下划线或单连字符)、.btn_disabled(下划线在 BEM 中无语义)、.btn:disabled(伪类不等于可编程状态,JS 无法控制、无法组合)。
.btn--disabled 必须配合显式 DOM 属性使用:
- HTML 中应同时写 disabled 属性(满足 WCAG)
- CSS 中至少包含 pointer-events: none 和 opacity: 0.5 等视觉反馈
- JS 控制必须用 element.classList.toggle('btn--disabled', isDisabled),避免字符串拼接漏空格
error 状态不能单独作为 Block,必须绑定具体业务上下文
error--network 或 .error 这类命名非法——error 不是 Block,它没业务含义、不可复用、极易被其他模块的 .error 覆盖。
正确做法是把 error 绑定到真实组件上:
立即学习“前端免费学习笔记(深入)”;
-
search-form--error:整个表单进入错误态 -
search-form__input--error:仅输入框高亮报错 -
user-card--error:卡片加载失败时的占位反馈
修饰符名要聚焦 UI 表现,不是后端返回码:
- ✅ --error、--invalid、--missing
- ❌ --network-failed、--404、--pending
多个状态共存时,CSS 必须显式声明组合规则
class="btn btn--primary btn--disabled btn--error" 合法,但浏览器不会自动合并效果。如果 .btn--disabled 设了 opacity: 0.4,而 .btn--error 设了 border-color: red,两者互不影响;但若都改了 background,后声明的会覆盖前一个。
需要叠加效果时,必须手写组合选择器:
.btn--disabled.btn--error {
opacity: 0.3;
box-shadow: inset 0 0 4px rgba(255, 0, 0, 0.3);
}关键点:
- 所有修饰符选择器必须是单类名:.btn--disabled { },不能写成 .btn .btn--disabled { }
- 避免修饰符之间重复定义同一属性(如都设 cursor)
- 构建工具(如 PurgeCSS)需在 safelist 中加入正则:/btn--(disabled|error|loading)/,否则动态添加的类可能被删
别把 loading 当通用前缀,它必须是独立 Block 或依附于具体组件
loading--spinner 合法的前提是 loading 是已定义的 Block,比如一个封装了动画和尺寸的占位容器;但如果只是给按钮加加载态,就不能硬塞 loading--spinner,而该用 btn--loading。
更常见且推荐的做法是依附式命名:
- user-card--loading(卡片正在加载)
- search-form--loading(搜索表单展示 skeleton)
- btn--loading(按钮内嵌 spinner 图标 + 禁用交互)
注意:btn--loading 和 btn--disabled 可共存,但若此时需禁用动画,就得重置:.btn--error.btn--loading .spinner { animation: none; }
真正容易被忽略的是:修饰符名必须对应可观察的 UI 变化。写 --busy 或 --pending,别人读代码时无法判断是否该显示转圈图标、是否禁用点击——这类词暴露实现细节,破坏组件契约。


















