必须隐藏原生 radio/checkbox 并用 label + :checked 伪类控制样式;display: none 最安全,需确保 input 与 label 兄弟关系且正确绑定,禁用态须单独处理。

不能直接样式化 或 元素本身,必须隐藏原生控件 + 用 搭配 :checked 伪类控制视觉状态。
为什么 display: none 是最安全的隐藏方式
用 visibility: hidden 或 opacity: 0 会保留占位、影响布局,且部分屏幕阅读器仍可能读取;display: none 彻底移除渲染流,同时保持语义和可访问性(前提是 label 正确绑定)。position: absolute; left: -9999px 也可行,但需额外处理焦点管理;display: none 更简洁、兼容性更好(IE9+ 完全支持)。
常见错误现象:input 被设为 visibility: hidden 后,点击 label 无响应 —— 因为原生控件虽不可见但仍占据空间,相邻选择器 :checked + label 失效;或使用 opacity: 0 导致 iOS Safari 点击穿透失效。
- 务必确保
和是兄弟关系(同级、input在前),否则:checked + label不匹配 -
for属性不是必须的,但推荐保留:它让点击 label 文本也能触发选中,提升可用性 - 禁用状态(
disabled)下:checked不会触发,所以需单独写input:disabled + label样式
:checked + label::before 是最常用且可控的绘制方式
用伪元素替代背景图或 SVG,避免资源请求、缩放失真,也便于动态换色。核心是利用 ::before 绘制外框,再用 :checked + label::after 或 ::before 的 content 叠加选中态(如圆点、对勾)。
参数差异示例:
input[type="radio"] + label::before {
content: "";
display: inline-block;
width: 18px;
height: 18px;
border: 1px solid #999;
border-radius: 50%;
margin-right: 8px;
}
input[type="radio"]:checked + label::before {
background-color: #007bff;
border-color: #007bff;
}
input[type="radio"]:checked + label::before::after {
/* ❌ 错误:伪元素不能再嵌套伪元素 */
}
正确做法是复用同一个 ::before,通过 content 和 background 切换:
input[type="radio"]:checked + label::before {
background-color: #007bff;
border-color: #007bff;
}
input[type="radio"]:checked + label::before::after {
/* 不可行,改用单层伪元素 + content 字符 */
}
/* ✅ 改为: */
input[type="radio"]:checked + label::before {
background-color: #007bff;
border-color: #007bff;
}
input[type="radio"]:checked + label::before {
content: "•"; /* Unicode 圆点 */
color: white;
font-size: 12px;
line-height: 18px;
text-align: center;
}
-
content: "\2713"(✓)适合 checkbox,"\2022"(•)或"\25CF"(●)适合 radio - 避免在伪元素中用
width/height+background-image混合,容易在高 DPI 屏幕上模糊 - 如需动画(如缩放选中态),给伪元素加
transition: transform .2s,不要动width/height
复选框与单选框的关键样式差异点
两者结构一致,但视觉逻辑不同:radio 强调「唯一性」,常需圆形外框 + 中心实心圆;checkbox 强调「状态切换」,需方角外框 + 对勾符号。若混用同一套 class,容易出现尺寸错位或语义混淆。
性能影响很小,但要注意:
- radio 组必须共用
name属性,否则无法互斥;CSS 层面不校验这点,全靠 HTML 正确性 - checkbox 可独立存在,无需
name一致,但 JS 获取值时依赖name分组 - 用
box-shadow: inset模拟 checkbox 内凹效果比background更轻量,且支持透明边框 - 移动端点击热区至少保证
44×44px,label 的padding或min-width/min-height要补足
disabled 状态下样式容易被忽略的细节
:disabled 元素不会触发 :checked,所以 input:disabled:checked + label 永远不生效。必须单独写 input:disabled + label 来覆盖正常态样式,并手动叠加「已选中 + 禁用」的视觉表现(比如半透明对勾)。
常见错误:只写了 input:disabled + label { opacity: .5; },结果 disabled 且 checked 的选项看起来和未选中一样 —— 用户无法分辨是否已被预设。
- 推荐方案:用
input:disabled + label设灰阶底色,再用input:disabled:checked + label::before显式绘制浅色对勾或圆点 - 不要依赖
pointer-events: none,它会阻断屏幕阅读器的交互 - 测试时务必手动在 HTML 中添加
disabled和checked同时存在的组合
真正麻烦的从来不是画出那个圆或对勾,而是让所有状态(默认 / 选中 / 禁用 / 禁用+选中 / 聚焦)在各种设备和辅助技术下都行为一致、语义清晰。别省略 for 和 name,别跳过 disabled + checked 组合测试,这两处漏掉,上线后用户反馈第一波就冲着这儿来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











