BEM通过强制使用单个类名选择器将CSS特异性锁死在0-1-0。所有样式必须直接作用于元素自身类名,禁用后代/子代选择器及全局通配符,Modifier须与Block/Element连写且共存于class属性中。

为什么BEM能天然卡死选择器权重在0-1-0
BEM 不是靠“少写几层”来降低权重,而是用命名结构把浏览器的 specificity 计算锁死在 0-1-0。只要所有选择器都只用单个类名(如 .header__logo),第二位数字就永远是 1——不管 HTML 结构怎么变、组件怎么挪,权重不会意外升到 0-2-0 或更高。
常见错误现象:.card .card__title 看似 BEM,实则已退化为后代选择器,权重变成 0-2-0;.button--primary:hover 虽然合法,但伪类会让权重升为 0-1-1,一旦和第三方库的 .btn:focus(也是 0-1-1)撞上,谁后声明谁赢,毫无确定性。
- 所有样式必须直接作用于元素自身类名,禁止空格分隔的后代/子代选择器
- 修饰符(Modifier)必须和 Block 或 Element 连写,如
.card--hovered,不能写成.card hovered - 需要
:hover/:focus时,优先用 JS 切换.button--hovered类,而不是在 CSS 里写伪类组合
如何检查并修复已存在的高权重选择器
打开 DevTools 的 “Computed” 面板,点开被划掉的属性,右键 → “Reveal in Sources”,直接定位冲突源。你会发现很多失效不是 BEM 失效,而是被 body .card .title 或 .theme-dark * { z-index: 0 !important; } 这类全局规则暴力覆盖了。
真正该修的不是 BEM 命名,而是这些破防点:
立即学习“前端免费学习笔记(深入)”;
- 禁用
*、body、div开头的全局选择器,尤其第三方主题包里的.theme-base * { } - HTML 中 Modifier 必须和基础类共存:
class="button button--disabled"✔️,class="button--disabled"❌(基础样式丢失,常表现为“没变化”) - 构建工具(如 css-loader)若配置
localIdentName时截断了--,会导致.button--disabled编译成.buttondisabled_abc,直接失效
多人协作中最容易悄悄破坏 BEM 权重的写法
不是故意写错,而是习惯性“补救式编码”。比如 Ant Design 的按钮样式不满足需求,有人直接写 .ant-btn { border-radius: 0 !important },这等于主动放弃语义隔离,后续所有覆盖都只能更暴力。
更可持续的做法是用 BEM 封装边界:
- 把第三方组件包进自己的 Block 容器:
<div class="my-form__submit-btn"><Button></Button></div>,再写.my-form__submit-btn .ant-btn(配合 CSS Modules 或自动哈希更稳) - 响应式隐藏不写
@media (max-width: 768px) { .nav { display: none !important } },而用 Modifier:.nav--hidden-mobile,由 JS 或媒体查询控制类名开关 - 避免手拼类名漏前缀,如 React 中写
className={`btn ${isDisabled ? 'disabled' : ''}`,正确应是btn--disabled
BEM 并不保证样式生效,它只保证责任归属清晰
.card__title--large 失效,90% 不是因为权重不够,而是 HTML 里没同时写 class="card__title card__title--large",或者被全局通配规则(如 body * { font-size: 14px !important })静默覆盖。
真正难控的从来不是权重数字,而是“谁该管这个样式”的模糊地带。BEM 把这个问题从运行时匹配逻辑,提前收束到类名定义阶段——card__title 属于 card 块,card--featured 是它的状态,card__title--large 是它的尺寸变体。这种归属一旦写错,样式就断层,但问题可定位、可修复,不会蔓延成权重军备竞赛。


















