伪类必须紧接元素选择器后,不可拆分为修饰符类名;正确写法为.btn__text:hover等,SCSS中用&:hover避免空格误写为后代选择器,多伪类可并列定义,禁用态需显式覆盖悬停样式以确保行为一致。

伪类必须直接写在Element选择器后,不能拆成Modifier
伪类如 :hover 和 :active 是浏览器原生机制,不是状态类名。写成 .btn--hover 或 .btn__icon-hover 是无效的——前者是BEM修饰符但没触发条件,后者根本不是伪类,只是个普通class,浏览器完全不会按悬停逻辑响应。
正确做法是把伪类追加在最终Element选择器末尾:.btn__text:hover、.btn__text:active。这既符合CSS语法,也保持BEM层级清晰。
- SCSS嵌套中用
&:hover最安全,避免手误写成.btn__text :hover(带空格就变成后代选择器) - 多个伪类可并列写:
&:hover、&:focus、&:active放在同一嵌套块里 - 不要给伪类加!important——它本就比普通class权重高,强行提升只会掩盖选择器结构问题
:hover/:active 和 --hover/--active 不能混用
:hover 描述的是瞬时、不可控的交互瞬间;而 --hover 这类修饰符代表的是可编程、可持久化的状态。二者语义不同,强行共存容易导致样式冲突或行为错乱。
常见错误现象包括:.btn--hover:hover 这种写法让人无法判断当前是“真悬停”还是“JS标记的伪悬停”,测试时也难断言真实交互路径。
立即学习“前端免费学习笔记(深入)”;
- 纯视觉反馈(颜色变化、位移、阴影)交给
:hover和:active - 需要跨元素联动(比如悬停卡片时显示侧边操作栏)、服务端预渲染、或与权限逻辑耦合的状态,才用
--elevated、--interactive这类修饰符 -
.card--hover .card__footer这类组合有效,但前提是--hover由JS严格控制增删,且不和:hover的transition属性冲突(例如同时设transform可能导致鼠标移出时卡顿)
禁用态下必须显式覆盖:hover行为
:disabled 对 button 有效,但对 div、span 等非表单元素无效。BEM组件常通过 --disabled 修饰符模拟禁用效果,此时若不主动重置 :hover,悬停样式仍会生效,违背用户预期。
正确写法是显式覆盖:
.btn--disabled:hover {
background-color: inherit;
color: #999;
cursor: not-allowed;
transform: none;
}
- 不要依赖
pointer-events: none一劳永逸——它会阻断:focus和键盘导航,破坏无障碍 - 如果组件含子元素(如图标、文字),需逐个重置其
:hover行为,或统一用.btn--disabled *:hover(注意权重和性能) -
--disabled和:hover共存时,确保禁用态样式优先级更高,否则鼠标悬停仍可能短暂闪现旧样式
伪类没有命名空间,全局样式可能意外覆盖
BEM的Block/Element命名能隔离样式作用域,但 :hover 本身不具备这种能力。比如某第三方reset.css写了 a:hover { text-decoration: underline; },即使你用了 .link__text:hover,也可能被覆盖。
这不是BEM的问题,而是CSS层叠的本质限制。
- 检查构建产物中是否存在全局
:hover规则(尤其来自normalize.css、tailwind preflight或UI库) - 必要时用更具体的选择器,例如
.nav__item > a:hover,而非仅.nav__link:hover - 避免在伪类里写过于宽泛的声明(如
all: unset),它会抹掉继承链上的关键样式
:hover 在触摸屏上基本不可靠,:active 在移动端可能延迟或失效。如果你的组件需要全端一致反馈,不能只依赖伪类。


















