开关按钮本质是伪装的,通过隐藏原生样式并用伪元素构建滑块与轨道实现视觉效果,需确保可访问性、焦点交互及响应式可用性。

开关按钮本质是伪装的 <input type="checkbox">
浏览器原生没有「开关」控件,所有看起来像 iOS 风格 toggle 的开关,底层都是 <input type="checkbox"> + CSS 隐藏原生样式 + 自定义伪元素。直接写 <input type="toggle"> 会失效,也不存在这种类型。
关键不是“怎么做开关”,而是“怎么让复选框看起来不像复选框”。原生复选框的勾选状态(checked)天然可用,无需 JS 监听 click 或模拟状态,这是最稳妥的起点。
隐藏原生复选框但保留可访问性
用 opacity: 0 或 visibility: hidden 会破坏屏幕阅读器识别;用 display: none 会让它完全脱离交互流。正确做法是视觉上移出视口,同时保持焦点和语义完整:
-
position: absolute+left: -9999px(最常用,兼容性好) - 或
clip-path: inset(100%)(更现代,但 IE 不支持) - 必须保留
for属性指向id,或确保<label></label>包裹<input>,否则点击轨道无法触发状态切换
用 ::before 和 ::after 构建滑块与轨道
开关的“滑动”效果靠两个伪元素配合实现:一个作背景轨道(::before),一个作滑块(::after)。它们都挂载在 <label></label> 上,通过 input:checked + label ::after 控制滑块位置。
常见错误是把伪元素加在 input 上——input 无法承载 ::before/::after(它是替换元素)。
示例关键片段:
input[type="checkbox"] {
position: absolute;
left: -9999px;
}
input[type="checkbox"] + label {
position: relative;
display: inline-block;
padding: 0;
}
input[type="checkbox"] + label::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 50px; height: 24px;
background: #ddd;
border-radius: 12px;
}
input[type="checkbox"]:checked + label::before {
background: #4CAF50;
}
input[type="checkbox"] + label::after {
content: '';
position: absolute;
top: 2px; left: 2px;
width: 20px; height: 20px;
background: white;
border-radius: 50%;
transition: transform 0.2s ease;
}
input[type="checkbox"]:checked + label::after {
transform: translateX(26px);
}
响应点击区域与键盘操作
用户实际点击的是 <label></label>,不是 <input>。如果只给伪元素设宽高,没给 label 设最小尺寸,点到边缘可能失灵。另外,键盘用户依赖 Tab 进入、Space 切换,所以 label 必须能获得焦点:
-
label要有明确的min-width和min-height(比如min-height: 24px) - 加
cursor: pointer提示可点击 - 不要用
pointer-events: none在伪元素上,否则会阻断点击穿透 - 若需禁用,应设
input:disabled并同步控制label的 opacity 和pointer-events
真正难的不是画出滑块,而是让它的尺寸、对齐、过渡节奏在不同缩放比和高对比度模式下依然可用。别为了酷炫牺牲 focus outline 或 contrast ratio。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











