纯 css 实现开关按钮的核心是用 checkbox 的 :checked 状态驱动 label 伪元素视觉变化;需隐藏 input 但保留可访问性,用 opacity: 0 + pointer-events: none;滑块位移用 transform 或 left 控制,避免 margin 引发重排;须适配移动端点击热区、深色模式及键盘焦点。

纯 CSS 实现开关按钮,核心不是“画一个按钮”,而是用 input[type="checkbox"] 的 :checked 状态驱动视觉变化——原生控件必须保留语义和可访问性,样式则完全由 label 和伪元素接管。
为什么不能直接给 input[type="checkbox"] 加宽高和圆角
Chrome、Safari 甚至新版 Firefox 都会忽略对 input[type="checkbox"] 直接设置的 width、height、border-radius,因为 UA 样式表强制覆盖。强行写只会导致布局错位或样式不生效,不是你 CSS 写错了,是浏览器故意不让你动它。
正确做法是隐藏它但不破坏可访问性:
- 用
opacity: 0+pointer-events: none(保留焦点、屏幕阅读器可读) - 不用
display: none或visibility: hidden(前者移除可聚焦性,后者仍占空间且无点击穿透) -
label必须通过for属性或包裹结构与该input关联,否则点击无效
用 ::before 和 ::after 画开关轨道和滑块
把 label 当作容器,用两个伪元素分别实现轨道(背景)和滑块(圆钮)。关键点在于:滑块的位移必须靠 transform: translateX() 或 left 控制,不能靠 margin——后者会触发重排,动画卡顿。
示例片段:
label.switch {
position: relative;
display: inline-block;
width: 60px;
height: 30px;
}
label.switch::before {
content: "";
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: #ccc;
border-radius: 15px;
transition: background 0.2s;
}
label.switch::after {
content: "";
position: absolute;
top: 50%; left: 2px;
width: 26px; height: 26px;
background: white;
border-radius: 50%;
transform: translateY(-50%);
transition: all 0.25s cubic-bezier(0.3, 1.2, 0.7, 1);
}
input:checked + label.switch::before {
background: #4caf50;
}
input:checked + label.switch::after {
left: calc(100% - 28px);
}
注意:left: calc(100% - 28px) 中的 28px = 滑块宽度(26px)+ 右侧留白(2px),这个计算必须精确,否则移动端拖动时容易错位。
移动端点击区域小、滑动不跟手?补这三处
绝对定位方案在手机上出问题,90% 是交互层缺失,不是 CSS 写得不对。
- 给
label加cursor: pointer和-webkit-tap-highlight-color: transparent(消除 iOS 点击灰斑) - 给滑块伪元素(
::after)加touch-action: pan-x(禁用默认纵向滚动干扰,让手指横向拖动更灵敏) - 确保整个
label的点击热区 ≥ 44×44px(iOS 最小可点击尺寸),可通过padding或min-width/min-height扩展,别只靠视觉大小
深色模式和键盘焦点不能漏
很多人做完正常状态就收工,结果在深色系统下开关变黑底黑钮,或键盘 tab 到它时毫无指示——这属于可访问性硬伤。
必须加:
-
@media (prefers-color-scheme: dark)下重设轨道/滑块颜色 -
input:focus + label.switch::after加box-shadow: 0 0 0 2px #007aff(蓝色焦点环) - 若用
aria-label补充说明(如aria-label="启用夜间模式"),记得同步更新文案逻辑
伪元素无法绑定事件、不支持 :focus-visible(需 fallback 到 input:focus)、iOS Safari 对 appearance: none 支持不稳定——这些边界情况一旦上线就难排查,建议在真机上反复点按+拖动+键盘 tab 测试三遍。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











