根本原因是.is-active规则未限定作用域,导致全局样式冲突并破坏BEM修饰符与状态类的职责分离;正确做法是禁止全局.is-active,只写带上下文的选择器如.btn.is-active,并用Sass mixin封装复用逻辑。

为什么 .btn--primary.is-active 和 .btn--secondary.is-active 会互相覆盖
根本原因是 .is-active 规则没限定作用域,比如写了全局的 .is-active { opacity: 0.5; },那所有带 is-active 的元素都会被统一降透明度。BEM 修饰符(如 --primary、--secondary)本该各自定义完整状态样式,但一旦让 .is-active 担负视觉职责,就破坏了“修饰符管表现、状态类管信号”的分工。
常见错误现象:
-
.btn--primary.is-active和.btn--secondary.is-active共享同一套.is-active颜色或背景规则,导致主按钮和次按钮激活态长得一模一样 - CSS 构建工具(如 PurgeCSS)扫描不到
.btn--primary.is-active这种组合,只看到孤立的.is-active,结果在生产环境误删了真实用到的状态样式
怎么写 CSS 才不冲突:必须绑定状态类到块或元素
状态类不是独立样式单元,它必须和具体块名或元素名共存才有意义。浏览器不认语义,只认选择器字符串是否匹配;.is-active 单独存在,就是个无上下文的炸弹。
正确写法要点:
立即学习“前端免费学习笔记(深入)”;
- 禁止写
.is-active { ... }这类全局规则 - 只写带上下文的选择器:
.btn.is-active、.tab__item.is-active、.user-card.is-loading - 如果需要复用逻辑,用 Sass mixin 封装,但调用时仍由使用者传入前缀:
.btn--primary.is-active { @include state-active($color: #fff); } - 确保 PurgeCSS 的
content配置里包含所有真实出现的组合,比如 HTML 中真有<button class="btn btn--primary is-active">
JS 切换 class 时容易漏掉什么
很多人用 el.classList.toggle('is-active'),却忘了同步处理 BEM 修饰符——尤其是 SSR 场景下,服务端已渲染出 .btn--primary,客户端 JS 再加 is-active,没问题;但如果服务端没加 --primary,而 JS 同时加了 --primary 和 is-active,顺序或时机不对就可能闪动或错位。
实操建议:
- 切换状态类时,不要只操作
is-active,也要确认基础修饰符是否就位,例如:el.className = 'btn btn--primary' + (isActive ? ' is-active' : '') - 避免用
el.classList.contains('is-active')做唯一判断依据,应结合el.matches('.btn--primary.is-active')或数据源状态 - 禁用态等需同步属性的场景,
.btn--disabled必须配disabled属性,不能只靠 class 控制视觉
要不要同时用 --active 和 is-active?怎么分工才不乱
可以共存,而且推荐共存,但必须切清边界:一个管 UI 变体,一个管运行时信号。
典型分工:
-
.btn--active定义加粗、底边框、背景色等视觉样式,可静态存在,SSR 直出即生效 -
.btn.is-active仅作为 JS 查询钩子(el.classList.contains('is-active'))、A11y 同步触发点(自动加aria-pressed="true")、或外部模块判断依据(如表单校验检查.step.is-active是否完成) - 两者不互相依赖:删掉
is-active不影响--active样式;删掉--active也不影响 JS 逻辑执行
最易被忽略的是:当团队开始混用 --active 和 is-active,没人再明确谁负责样式、谁负责信号,很快就会有人在 CSS 里偷偷给 .is-active 加颜色,又有人在 JS 里直接读 --active 判断状态——边界一旦模糊,冲突就从“能修”变成“不敢动”。


















