双连字符--是BEM中唯一合法的修饰符分隔符,用于明确标识状态或变体,确保工具链识别、语义清晰及协作一致;button--disabled✅、button__icon--spin✅,而button-disabled❌或button__icon-spin❌均违反规范。

双连字符--只用于修饰符,不是可选风格
双连字符 -- 是 BEM 中唯一合法的修饰符分隔符,它不是“看起来更酷”的写法,而是语义锚点:告诉任何人(包括人、IDE、Stylelint、PostCSS 插件)——后面这部分是状态或变体,不是新组件、也不是子元素。
常见错误现象:button-disabled 或 button_disabled 看似能用,但会直接破坏 BEM 的三层结构识别。工具链无法区分这是 button 的 disabled 状态,还是一个叫 button-disabled 的全新块;新人也容易误以为它是独立组件。
-
button--disabled✅ 表示 button 块的禁用状态 -
button__icon--spin✅ 表示 button 内部 icon 元素的旋转状态 -
button-disabled❌ 语义断裂,既不像块(缺命名空间),也不像 modifier(缺--) -
button__icon-spin❌ 混淆 element 和 modifier,__和--不能互换
为什么不能用单连字符-代替--
单连字符 - 在 BEM 中专用于块名内部单词分隔,比如 search-form、user-profile。一旦用 - 写修饰符,就和块名构造规则冲突,导致层级模糊。
使用场景举例:你有一个 nav-menu 块,想加个“折叠”状态。正确写法是 nav-menu--collapsed;如果写成 nav-menu-collapsed,就和 nav-menu-item 这类元素名在视觉上难以区分,也破坏了 “块名 + -- + 修饰符” 的固定模式。
立即学习“前端免费学习笔记(深入)”;
性能/兼容性影响不大,但协作成本极高:Stylelint 的 plugin-bem-linter 会直接报错;VS Code 的 CSS 类名跳转可能失效;团队新人看到 card-rounded 时,根本没法判断这是 card 的圆角变体,还是另一个叫 rounded-card 的组件。
修饰符必须依附于块或元素,不能裸奔
-- 前面必须有完整的块名或元素名,不能单独出现。它不是“通用开关”,而是上下文绑定的状态标识。
-
modal--fade-in✅ 修饰 modal 块的入场动画 -
modal__header--sticky✅ 修饰 modal 内部 header 元素的粘性行为 -
--loading❌ 没有归属,没人知道这是谁在加载 -
loading❌ 更糟,完全脱离 BEM 结构
容易踩的坑:有人为图省事,在 JS 中动态拼接类名,比如 el.classList.add('button--' + state)。如果 state 是 'large' 没问题,但要是传入 'primary-active' 就会产出非法类名 button--primary-active ——这不是两个修饰符,而是一个含糊不清的字符串。应该拆成 button--primary button--active 并分别控制。
动画类名也必须遵守--规则
所有动画类名只要参与 BEM 结构,就必须用 --,且不能混用驼峰或下划线。这是 PostCSS 动画插件、CSS-in-JS 库解析修饰符的基础。
例如:tab--enter-active 和 tab--leave-to 是标准写法;tab-enter-active 或 tab_enterActive 都会让自动化工具无法识别其为 tab 组件的动画状态,进而导致样式复用失败或热重载异常。
关键点在于:BEM 不是“让 CSS 更好看”,而是“让 CSS 可推理”。-- 是这个推理链条里最不可妥协的符号 —— 它不提供样式,但决定了别人能不能看懂你写的到底是什么。


















