BEM修饰符必须用--分隔,如.btn--disabled;禁止.btn-disabled或嵌套修饰符;多状态共存需空格组合并显式声明CSS叠加效果;伪类:hover等不应使用修饰符。

按钮状态类名必须用 -- 修饰符,不能写成 .btn-disabled
这是 BEM 最硬的语法边界:修饰符(Modifier)唯一合法分隔符是双连字符 --。写成 .btn-disabled 或 .btn_disabled,CSS 里它就不是修饰符,而是一个独立类名——构建工具可能误删、DevTools 无法识别其状态语义、协作时别人会当成新组件去改。
常见错误:.btn-primary-disabled 这种耦合命名,既违反 BEM 分离原则,又让 JS 控制状态时不得不拼字符串,极易漏空格或覆盖其他类;.btn--primary--disabled 是非法语法,BEM 不允许嵌套修饰符。
-
.btn--disabled、.btn--loading、.btn--success是唯一合规写法 - 多个状态共存时,用空格组合:
class="btn btn--primary btn--loading" - JS 添加/移除必须用
element.classList.toggle('btn--loading', isLoading),避免手动拼接
状态修饰符必须绑定视觉或行为表现,别用业务词如 --pending
BEM 修饰符不是后端响应码的映射,而是 UI 层可感知、可验证的行为信号。写 .btn--pending,没人知道它是否禁用点击、是否显示 spinner、是否灰掉文字;但 .btn--loading 一眼就知道:有加载指示、交互被抑制、视觉反馈明确。
同理,.btn--network-error 把网络层细节暴露到样式层,破坏关注点分离;.btn--busy 模糊不清,不如 .btn--submitting 或 .btn--saving 直接对应用户动作。
立即学习“前端免费学习笔记(深入)”;
- 优先用行为动词或视觉关键词:
--loading、--error、--success、--idle - 布尔型状态可用
--is-前缀,如.modal--is-open,但按钮更倾向阶段词:--submitting>--is-submitting - 禁止通配匹配:
[class*="is-"]会误伤第三方组件,且违背 BEM 显式作用域原则
多个状态共存时,CSS 必须显式声明组合效果
BEM 允许多修饰符共存,但 CSS 不会自动合并规则。如果 .btn--loading 设了 cursor: wait,.btn--disabled 写了 pointer-events: none,最终表现取决于层叠顺序,而非 class 顺序——这意味着你不能靠“写在后面”碰运气。
高频动态状态(如 loading)建议放在 CSS 文件靠后位置,避免被静态状态覆盖;需要叠加视觉效果时,必须单独写组合选择器。
-
.btn--disabled.btn--loading { opacity: 0.6; }—— 显式控制叠加态 -
.btn--error.btn--loading .spinner { animation: none; }—— 重置冲突动画 - 不要写
.btn--disabled--loading,这不是 BEM 合法语法,JS 也无法单独控制其中一种状态
伪类交互(如 :hover)不该用 BEM 修饰符管理
:hover、:focus、:active 是浏览器原生能力,轻量、无障碍友好、无需 JS 触发。用 .btn--hover 不仅冗余,还会导致测试难覆盖、样式逻辑割裂、且在键盘导航等场景下失效。
真正需要 JS 控制的状态,是那些无法由伪类表达的:比如异步加载中、服务返回失败、表单校验通过等。这些才属于 BEM 修饰符的管辖范围。
-
.btn:hover直接写,不加修饰符 -
.btn--disabled必须配合原生disabled属性,双重保障 - 禁用状态下的 hover 效果,应由
.btn--disabled:hover覆盖,而不是靠 JS 切换--hover


















