必须使用<input type="checkbox">作为状态源并用label正确关联,:checked伪类仅适用于checkbox和radio;动画应基于input:checked + label::after触发,仅过渡opacity和transform以保性能;移动端需确保触控区域≥44×44px且id与for严格匹配。

按钮点击后不触发 :checked?先确认它是 <input type="checkbox">
直接用 <button> 或 <div> 是没法响应 :checked 的——这个伪类只作用于表单控件中实际存在“选中态”的元素,目前只有 <input type="checkbox"> 和 <input type="radio"> 支持。:checked 不是给任意元素加个 class 就能模拟的。
常见错误现象:label + button 组合点击后样式没变、控制台没报错但动画完全不动。本质是 HTML 结构没把可交互控件和视觉层真正绑定。
- 必须用
<input type="checkbox">作为状态源,哪怕它被隐藏 -
label要通过for属性或包裹关系关联到该input,否则点击不触发状态切换 - 不要试图给
button添加checked属性——它根本不是布尔属性,浏览器会忽略
勾选动画靠伪元素实现:用 ::before 或 ::after 叠加图标
纯 CSS 动画不能直接“画出对勾”,得靠字体图标(如 Unicode ✅)、SVG 内联或 CSS 绘制路径。最轻量且兼容性好的方式是用 ::after 生成内容并配合 transform 控制显隐和缩放。
关键点在于:动画要发生在 input:checked + label::after 这类组合选择器下,而不是写在未选中态上。
立即学习“前端免费学习笔记(深入)”;
- 隐藏原生
input:用position: absolute; opacity: 0;,别用display: none,否则失去可访问性和状态监听 -
label承担点击区域和视觉容器角色,需设position: relative,方便伪元素绝对定位 - 伪元素默认
opacity: 0; transform: scale(0);,input:checked + label::after中改为opacity: 1; transform: scale(1);并加transition
input[type="checkbox"] { position: absolute; opacity: 0; }
label { position: relative; display: inline-block; padding-left: 32px; }
label::after {
content: "✓";
position: absolute;
left: 0; top: 50%; transform: translateY(-50%);
opacity: 0; transform: scale(0);
transition: all 0.2s ease;
}
input:checked + label::after {
opacity: 1; transform: scale(1);
}
:checked 动画卡顿或跳变?检查 transition 触发属性和重排
CSS 动画卡顿通常不是因为逻辑错,而是浏览器在反复重排(reflow)或触发了非合成层属性。比如对 width、height、top、left 做 transition,就容易掉帧。
使用场景里最容易被忽略的是:你改了 opacity 和 transform,但父容器有 overflow: hidden 或 will-change: auto,导致 GPU 合成层没启用。
- 只对
opacity和transform做过渡,它们是仅触发合成的属性,性能最稳 - 避免在伪元素上设置
margin或padding动画,这会强制重排 - 如果动画仍不顺滑,在伪元素上加
will-change: transform;(慎用,仅在必要时) - 移动端要注意
click有 300ms 延迟,建议加touch-action: manipulation;到label
移动端点击无响应?label 尺寸太小或缺少触控适配
桌面端看着好好的按钮,到手机上要点三四次才生效,大概率是 label 的可点击区域不足 44×44px —— iOS 和 Android 都有最小触控目标建议值。
另一个坑是:用 for 关联时,input 的 id 和 label 的 for 值不一致,或存在空格/大小写差异,导致点击失效。
-
label至少设min-width: 44px; min-height: 44px;,内部文字用line-height垂直居中 - 确保
input有明确id,label[for]与之完全匹配(区分大小写) - 别依赖
pointer-events: none来“透传”点击——它会让整个label失去响应能力 - 真机调试时打开 Safari 或 Chrome 的“模拟触摸”再测试,光靠鼠标悬停看不出问题
input 加 name 属性——表单提交时它不会被序列化,但更隐蔽的问题是:某些辅助技术(如屏幕阅读器)会因此忽略控件语义。动画再丝滑,用户不知道这是个开关,就等于没做对。










