bem 正确写法必须用 @at-root #{&}__element 输出单类名,禁用 &__element 嵌套(生成后代选择器)、禁用修饰符挂元素下、禁用伪类内再嵌套,核心是强制类名显式归属与 html 一致。

直接用 & 嵌套写 BEM,90% 的人第一行就产出 .card .card__title 这种带空格的后代选择器——它根本不是 BEM,而是嵌套陷阱。
为什么 &__title 在 SCSS 里会编译出错误选择器
SCSS 的 & 不校验语义,只做字符串拼接。在 .card { &__title { } } 中,& 确实是 .card,但默认行为是生成后代关系:.card .card__title(注意中间空格)。浏览器按「.card 下的 .card__title」解析,一旦 HTML 里 <div class="card__title"> 没包在 <code>.card 里,样式就失效。
-
.card { .header { &__logo { } } }→.card .header .card__logo:多出无意义层级,权重飙升 -
.btn:not(.disabled) { &__text { } }→.btn:not(.disabled) .btn:not(.disabled)__text:非法类名,被浏览器忽略 -
.card { &:hover { &__body { } } }→.card:hover .card__body:违背 BEM 扁平原则,且无法复用
@at-root #{&}__element 是唯一安全提级方式
想得到真正的 .card__title,必须跳出嵌套上下文,在 CSS 根层级生成单类名。@at-root 是最轻量、最可控的解法,#{&} 取的是块名本身(不含空格),再拼接 __ 或 --。
-
.card { @at-root #{&}__header { color: #333; } }→ 输出.card__header -
.card { @at-root #{&}--featured { background: gold; } }→ 输出.card--featured -
$block: "button"; @at-root #{$block}__icon { }→ 输出.button__icon
禁止在 @media、&:focus、&[disabled] 内部再用 @at-root #{&}__xxx,否则输出顺序错乱,且破坏 BEM 同级语义。
哪些 & 写法能用,哪些必须禁用
安全写法只有三类,其他一律高危:
-
&__element:仅限块级根选择器内直写,如.card { &__title { } }→.card__title -
&--modifier:修饰符必须作用于块本身,如.card { &--shadowless { } }→.card--shadowless -
&:hover、&:focus等伪类:仅用于显式状态控制,且需配合 JS 切换类名 - 禁用:
& .child、& > *、&__item a、&__item p—— 全部产出带空格选择器 - 禁用:
&:hover__icon(语法非法)、&__text--happy(修饰符不该挂元素下)
真正容易被忽略的是:BEM 的解耦效果不来自 & 或 @at-root 的语法糖,而来自对类名显式归属的强制约束——.card__title 必须出现在 HTML 的 class 属性里,漏掉基础类,调试时比逻辑错误更隐蔽。











