最稳妥的开关状态修饰符是--on和--off,语义清晰且符合BEM规范;需配合:checked伪类、ARIA属性(role="switch"、aria-checked)、焦点样式及touch-action等确保可访问性与交互一致性。

开关状态用 --on 和 --off 修饰符最稳妥
直接用 --on 表示开启、--off 表示关闭,是 BEM 社区最广泛接受的写法。它语义清晰、可读性强,且与 HTML 的 checked 属性天然对应——你不需要反向映射逻辑,避免“开状态却加 --disabled”这类认知错位。
常见错误是写成 --active/--inactive 或 --enabled/--disabled,但它们容易和禁用态(disabled 属性)混淆;也有人用 --checked/--unchecked,虽技术上准确,但暴露了底层实现细节,不符合 BEM “关注行为而非实现”的原则。
-
--on应始终对应用户感知的“已开启”,无论底层是input[type="checkbox"]还是纯div+ ARIA - 修饰符必须作用在开关的根元素(如
switch块),而不是内部的滑块或轨道 - 不要同时存在
--on和--off:一个开关实例只应有其中一种,否则样式层会冲突
CSS 中如何配合 :checked 自动切换修饰符?
纯 CSS 实现时,不能靠 JS 动态增删类名,得用伪类联动。典型做法是把 input[type="checkbox"] 隐藏,用相邻兄弟选择器绑定状态:
.switch__input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.switch--on .switch__track {
background-color: #409eff;
}
.switch__input:checked + .switch__track {
background-color: #409eff;
}
注意:这里 .switch--on 是为 JS 场景准备的(比如 SSR 或初始化状态),而 :checked 规则负责实时响应用户点击。两者共存时需确保样式权重一致,否则会出现“JS 设了 --on,但 CSS 规则没生效”的情况。
立即学习“前端免费学习笔记(深入)”;
- 如果完全依赖 CSS,就别加
--on/--off类名,只用:checked控制样式 - 如果需要 JS 控制(如表单重置、受控组件),务必同步更新类名和
input.checked属性,否则视觉与实际值不一致 - 使用
label[for]或包裹input保证点击区域可访问,否则:checked不会触发
为什么不用 is-on 这类布尔型前缀?
BEM 官方明确不推荐 is- 前缀修饰符(如 is-on),因为它是 SMACSS 或 OOCSS 的命名习惯,不是 BEM 的一部分。BEM 的修饰符本质是“该块的某种变体”,--on 直接描述这个变体是什么,而 is-on 描述的是“是否处于某状态”,属于动词性判断,破坏了 BEM 的名词化约定。
更实际的问题是工具链兼容性:is- 在 PostCSS、BEM 工具(如 bem-sdk)或 IDE 插件中可能无法被正确识别为修饰符,影响自动补全或 CSS 模块化处理。
- 所有 BEM 工具链(包括
bem-tools、postcss-bem)默认只识别双连字符--开头的修饰符 -
is-类名在 CSS-in-JS(如 styled-components 的 BEM 插件)中常被忽略,导致样式隔离失效 - 团队协作时,混合使用
--on和is-on会增加认知成本,尤其对新成员
移动端触摸反馈和焦点管理容易漏掉什么?
开关在移动设备上点击后没反馈、键盘 Tab 无法聚焦、屏幕阅读器读不出状态——这些问题往往不是修饰符命名导致的,而是配套属性缺失。
关键点不在 --on 怎么写,而在它是否和 ARIA、focus 样式、touch-action 协同工作:
- 必须给开关根元素加
role="switch",并用aria-checked="true/false"同步状态,否则 VoiceOver/NVDA 不识别 -
.switch:focus-visible .switch__thumb要有明显轮廓,否则键盘用户找不到当前焦点 - 在 iOS 上,若未设置
touch-action: manipulation,快速点击可能触发 300ms 延迟或误判为拖拽 - 不要用
user-select: none锁死整个开关,否则会影响屏幕阅读器的文本朗读范围
--on 只是起点,漏掉任一环,用户看到的都可能是一个“看起来开了,其实没开”的假开关。


















