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:视觉隐藏,语义保留。