必须使用<input type="checkbox">实现开关按钮,因其提供原生二态语义、键盘导航、空格切换、表单提交及屏幕阅读器支持;用div或button模拟会导致可访问性与功能全面失效。

纯 CSS 能实现开关按钮,但必须用 <input type="checkbox">,不能用 <div> 或 <button> 模拟——否则表单提交、键盘空格切换、屏幕阅读器识别全会失效。
为什么一定要用 input[type="checkbox"]?
它提供原生的二态语义和交互能力::checked 伪类可直接驱动样式变化,且支持键盘焦点、Tab 导航、空格键切换。绕过它用 div + JS,等于放弃表单集成、可访问性和默认行为。
- 未勾选时,
value不会随 form 提交(除非额外加 hidden input) -
aria-checked、role="switch"需手动维护,易出错 - 移动端双击放大、iOS 语音控制等系统级交互全部丢失
:checked + label 选择器怎么写才稳定?
推荐结构:把 <input> 放在 <label> 前面,用相邻兄弟选择器 input:checked + label。这样 label 可自由设宽高、背景、伪元素,不受 input 嵌套位置限制。
- 避免写成
label input:checked——一旦 input 移出 label 或中间插其他元素,样式立刻失效 -
for和id必须严格匹配,否则点击 label 不触发状态切换 - label 点击热区要足够大(建议最小 44×44px),并加
user-select: none防止误选文字
滑块动画靠什么实现?
核心是 transform: translateX() + transition,不是 left/top 位移。前者触发 GPU 加速,动画更顺;后者触发 layout,容易卡顿。
立即学习“前端免费学习笔记(深入)”;
- 初始状态:
label::after { transform: translateX(0); } - 选中后:
input:checked + label::after { transform: translateX(1.5em); }(数值按容器宽度微调) - transition 推荐写全属性:
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),比ease更自然 - 别忘了给 label 设
position: relative,否则伪元素定位会乱
真正难的不是动效,而是让开关在不同缩放、高对比度模式、强制颜色下依然可识别——这需要测试 focus outline、prefers-reduced-motion 媒体查询、以及 color scheme 适配,不是加个 transition 就完事了。


















