纯 css 可实现自定义单选按钮打勾效果,需隐藏原生 radio 并通过关联 label 的 ::before/::after 绘制圆圈和对勾;因浏览器禁止对 input 表单控件使用伪元素,必须用 label 作为样式载体,配合 position: relative、transform: translatey(-50%) 实现精准居中,并扩展点击区域与保障无障碍。

纯 CSS 实现自定义单选按钮的打勾效果,完全可行,但必须隐藏原生 <input type="radio">,用 ::before 或 ::after 在关联的 <label></label> 上绘制勾选图标——浏览器不支持直接对 <input> 伪元素样式化,这是最常踩的坑。
为什么不能直接给 input[type="radio"] 加 ::after?
所有主流浏览器(Chrome/Firefox/Safari)都不允许对表单控件(input、select 等)使用 ::before/::after,伪元素会失效。强行写进去,调试器里能看到规则,但视觉上什么也不出现。
- 必须用
input[type="radio"]的相邻或父级<label></label>作为样式载体 - 推荐结构:
<input id="opt1" type="radio" name="theme"><label for="opt1">深色模式</label> - 关键 CSS:先用
input[type="radio"] { opacity: 0; position: absolute; }隐藏它,再通过label::before绘制圆圈,input:checked + label::after绘制对勾
input:checked + label::after 打勾图标的尺寸与定位怎么调才不偏?
对勾是绝对定位在 label 内部的,容易因行高、字体大小、padding 变动而错位。最稳的方式是把 label 设为 position: relative,再让 ::after 基于它定位。
-
label { position: relative; padding-left: 32px; }(留出图标空间) label::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 20px; height: 20px; border: 2px solid #999; border-radius: 50%; }input:checked + label::after { content: "✓"; position: absolute; left: 4px; top: 50%; transform: translateY(-50%); font-size: 14px; color: #2563eb; }- 注意:
✓是 Unicode 字符,兼容性好;若要用 SVG 或 background-image,需额外处理宽高和居中
移动端点击区域太小,怎么扩大又不破坏语义?
原生 input 隐藏后,仅靠 label 文本区域响应点击,手指点不准。不能加 padding 到 input 上(无效),也不能把 label 的 padding 设太大影响布局。
- 正确做法:给
label::before加pointer-events: none;,再用label自身的padding或min-height扩展可点区域 - 更稳妥的是:在
label外层包一层<div class="radio-group">,并设 <code>min-height: 44px(iOS 最小触控建议值) - 别忘了加
cursor: pointer到label,视觉反馈更明确 - 无障碍方面:确保
for属性匹配id,且label文本清晰描述选项
真正麻烦的不是画勾,而是让不同字号、行高、缩放比例下都稳稳居中;transform: translateY(-50%) 是目前最可靠的垂直居中方案,但前提是父容器有明确高度或使用 top: 50% 配合它——这点很容易被忽略,一换字体就偏移。











