直接用:hover,禁用.btn--hover;bem修饰符仅表达持久状态(如--loading、--disabled),而:hover是瞬时伪类,具原生交互与可访问性支持,二者语义与用途严格分离。

伪类:hover和修饰符--hover哪个该用?
直接用 :hover,别写 .btn--hover。BEM 修饰符不是用来镜像伪类状态的,它只表达 UI 层可感知、可控制的**持久状态**,比如加载中、禁用、成功反馈;而 :hover 是瞬时、由用户交互触发、不可被 JS 主动设置的 CSS 行为。
常见错误现象:.btn--hover 在 JS 里手动加类模拟悬停效果,结果鼠标移开后类还挂着,视觉与实际交互脱节;或者想用它做无障碍聚焦态(:focus),但屏幕阅读器无法识别这个类,语义断裂。
-
:hover、:focus、:active这类伪类必须原生使用,它们有浏览器内置的触发逻辑和可访问性支持 - 只有当某个“悬停效果”需要跨组件同步、或需在非悬停场景下强制激活(如调试、自动化测试、无障碍高对比模式)时,才考虑用修饰符,此时应命名为
.btn--is-hovered(带is-前缀,明确是状态快照,而非行为) - 如果按钮同时有
:hover和.btn--disabled,CSS 必须显式写出.btn--disabled:hover规则,否则禁用状态下悬停样式会被继承或覆盖错乱
表单验证该用 :invalid 还是 --invalid 修饰符?
一律用 field-email--invalid 这类修饰符,不用 :invalid。原生伪类在空 required 字段上初始为 valid,移动端 Safari 对 :user-invalid 支持差,且它无法联动容器、图标、错误文案等周边元素。
实操要点:
- 修饰符必须挂在字段级 block 容器上,例如
<div class="field-email field-email--invalid">,而不是 input 元素本身 <li>JS 校验后只操作一个 class:<code>fieldEl.classList.toggle('field-email--invalid', !isValid),干净可控 - 避免把修饰符挂到
form上——一个字段错就整表单变红,语义爆炸 - 多个验证规则共存时(空、格式错、域名不符),不拆成
--empty、--format,统一用--invalid,具体错误类型由aria-describedby和错误文案承载 - 如果伪元素内容靠
content生成(如"→"或 SVG data URL),它只是表现层行为,**不参与 BEM 结构命名**——别因此定义.list__separator - 禁止写
.button__arrow::before却没对应 HTML 节点,这破坏 BEM “元素必须真实存在” 的约束 - 需要差异化控制(如
.btn--primary::after和.btn--ghost::after不同箭头方向),直接在修饰符上叠加伪类即可,无需新增中间元素层 - 禁用所有现代伪类,改用显式修饰符兜底,例如把
.card:not(.card--disabled) .card__button拆成两个独立规则:.card .card__button和.card--disabled .card__button { display: none; } - 所有修饰符对应的样式必须带 IE 兼容前缀,比如
.button--loading同时声明display: -ms-flexbox和display: flex - 高级特性(如渐变文字)用
@supports包裹,并提供降级类:.heading--gradient-fallback,不能指望 IE 自动回退
::before / ::after 该挂块级还是元素级选择器?
看伪元素是否依赖真实 DOM 结构:装饰整个块(如卡片阴影、标题下划线)用 .card::after;强化某个已有元素(如输入框右侧清空图标)用 .input__clear::before,前提是 .input__clear 真实存在且语义合理。
关键判断点:
IE11 下 BEM 类名能用,但为啥样式不生效?
BEM 类名本身在 IE11 完全兼容,问题出在配套 CSS 特性上::not()、:first-child、Flex Gap、background-clip: text 这些才是真凶。命名再规范也救不了底层能力缺失。
实操建议:
BEM 的价值不在类名长度,而在结构契约——一旦你开始用 JS 拼接类名、靠选择器“碰巧命中”,或者把修饰符当伪类镜像,就等于放弃了它最核心的可预测性。











