开关按钮的布尔修饰符必须用--checked,因其直接映射原生checkbox的DOM状态,语义明确、可测试且与:checked伪类对齐;禁用--on或--active以避免业务歧义与样式冲突,并须同步input.checked、aria-checked及SSR初始状态。

开关按钮的布尔修饰符必须用--checked,不能写成--on或--active
因为--checked直接映射原生<input type="checkbox">的 DOM 状态,语义明确、可测试、与伪类:checked天然对齐。写成--on会让人误以为是业务逻辑开关(比如“开启通知”),而--active又和:active伪类冲突,导致样式不可预测。
常见错误:.switch--on在 SSR 首屏渲染时无法同步状态(服务端不知道用户偏好),且 JS 切换后容易漏掉input.checked = true同步,造成视觉与真实值不一致。
-
--checked只用于表达“当前被选中”的确定状态,不是“准备启用”或“已生效” - 禁止搭配
data-属性模拟:.switch[data-state="checked"]绕过 BEM 语义,构建工具可能误删 - 必须和
input[type="checkbox"]绑定,不能仅靠 class 控制——否则键盘用户无法用空格切换
.switch__track和.switch__thumb的修饰符要分开定义,不能共用--checked
开关的视觉反馈分两部分:轨道(track)变色、滑块(thumb)位移。若统一用.switch--checked .switch__thumb,会导致选择器权重过高,后续加--disabled时难以覆盖;若只写.switch__thumb--checked,又割裂了状态归属。
正确做法是让状态类落在根块上,子元素通过上下文继承行为:
立即学习“前端免费学习笔记(深入)”;
-
.switch--checked .switch__track控制背景色和边框 -
.switch--checked .switch__thumb控制transform: translateX()和阴影 - 禁用态单独加
.switch--disabled,组合写为.switch--disabled.switch--checked .switch__thumb显式重置位移
布尔修饰符必须声明pointer-events: none和opacity,但不能碰color或border
禁用开关时,用户不能点击,但需保留视觉可读性。BEM 布尔修饰符只负责“交互锁定+基础降权”,具体颜色应由 CSS 变量承接,避免硬编码。
- 必须声明:
pointer-events: none、opacity: 0.6、cursor: not-allowed - 可以声明:
background-color: var(--switch-track-disabled)(用变量承接主题色) - 禁止声明:
color(文字属于.switch__label,应由其自身修饰符控制)、border(若需弱化边框,走--border-subtle键值型修饰符) - 记得覆盖伪类:
.switch--disabled:hover .switch__thumb必须显式设transform: none,否则悬停仍会触发过渡
JS 切换--checked时,必须同步更新input.checked和aria-checked
单纯操作 class 会让辅助技术丢失状态,也破坏表单提交逻辑。BEM 修饰符是 UI 表现层的钩子,不是状态源。
- 添加类前,先执行
input.checked = true,再element.classList.add('switch--checked') - 移除类时同理:
input.checked = false→classList.remove('switch--checked') - 必须同步
aria-checked属性:input.setAttribute('aria-checked', 'true') - 禁止用
className = 'switch switch--checked'字符串赋值——易覆盖其他修饰符如--disabled
最易被忽略的是 SSR 首屏:服务端渲染时,input的checked属性必须和服务端判断出的初始状态一致,否则 hydration 后 React/Vue 会强制重置 DOM,造成闪动或状态错乱。


















