switch · 开关
开关用于立即生效的设置项("开/关通知")。 它与 checkbox 的差别不是形状,是语义 —— 读屏会播报「开关,已开启」而不是「复选框,已勾选」。
基本用法
当前:开
只有滑块(文字视觉隐藏,语义保留)
看起来只有一个滑块,但读屏仍会念「暗色模式,开关,已开启」
两档尺寸
禁用态
键盘行为(APG Switch Pattern)
Space 切换 —— 这是唯一能切换的键。
Enter 不切换(button 默认行为会切,但 switch 不是 button)。
本组件用原生 checkbox 实现,所以浏览器天然只响应 Space —— 不需要写 JS 去拦 Enter,也就不会有"忘拦"的风险。 这是"用原生控件"最大的收益:无障碍行为免费且必然正确。
反例
❌ div + role="switch",自己管所有状态
开启动价推送
看起来和下面那个一模一样 —— 这正是坑所在。
常见写法是 <div role="switch" tabindex="0" onclick="…">
再手写 aria-checked 切换,于是 Space 键、焦点样式、状态播报
三件事都得自己管,漏一件就坏。
而 axe 只看得见 DOM,看不见你的 JS 有没有绑对。
✅ 原生 checkbox + role="switch"
同一个外观,但键盘响应、焦点环、状态播报全部由浏览器给 —— 少写的那部分不是"省事",是"少了一个会忘的地方"。
反例:给 label 加 display:none
为了"只留滑块"而把 <label> 隐藏掉,
开关就失去了可点击的文字区域,也失去了语义名字 ——
读屏只会念「开关」,不知道它是干什么的。
正确做法是 .switch__label--sr:视觉隐藏,语义保留。