:checked + label 是最稳定零 JS 的纯 CSS 自定义单选框方案;因原生 radio 无法直接样式化且 appearance: none 不可靠,需用相邻兄弟选择器精准控制对应 label。

:checked + label 是目前最稳定、零 JS、语义完整且无障碍友好的纯 CSS 自定义单选框方案。它不依赖 JavaScript,也不破坏表单可访问性,只要结构和样式写对,就能在所有现代浏览器中可靠工作。
为什么不能直接给 input[type="radio"]:checked 加样式?
浏览器对原生 input[type="radio"] 的渲染控制极弱——width、height、background、border-radius 基本无效,::before/::after 也完全不支持。直接写 input[type="radio"]:checked { background: red; } 不会看到任何变化,因为该元素本身没有可绘制的视觉盒模型。
- 原生 radio 是 UA(User Agent)控件,样式由操作系统或浏览器内核硬编码决定
- 即使加
appearance: none,在 Safari 和部分旧版 Chrome 中仍可能残留不可控渲染行为 -
display: none会切断键盘焦点和屏幕阅读器路径,必须避免
为什么必须用 + label 而不是 ~ label 或其他选择器?
+ 是相邻兄弟选择器,它能精准绑定“当前选中的那个 input 紧接着的下一个 label”,避免误触同组其他项。而 ~(通用兄弟)会匹配后面所有同级 label,导致多个 label 同时响应一个选中状态。
- 结构必须是
<input><label for="xxx">...</label>或<label><input>...</label>,否则+不生效 - 如果用
input:checked ~ label,点选option1会让option2和option3的label全部变色 - 嵌套结构(
label包裹input)虽可省略for/id,但某些辅助技术对嵌套 label 的支持不如显式for稳定
为什么推荐 opacity: 0 + position: absolute 而非 display: none?
display: none 会让元素彻底退出可访问树,键盘 Tab 跳过、屏幕阅读器读不到、:focus 失效。而 opacity: 0 配合 position: absolute 保留了所有交互能力,只是视觉上不可见。
立即学习“前端免费学习笔记(深入)”;
-
opacity: 0保持 DOM 可聚焦、可被 AT(Assistive Technology)识别 -
position: absolute防止占位空白影响布局,比visibility: hidden更干净 - 别漏掉
width: 1px; height: 1px;,防止某些浏览器仍渲染微小占位 - 必须配合
cursor: pointer和足够padding给label,否则点击热区太小,移动端体验差
单选组互斥和样式隔离怎么保证?
单选组靠 name 属性维持互斥逻辑,但 CSS 样式隔离得靠更精细的选择器。通配 input:checked + label 会污染整组,必须按 value 或 id 区分。
- 同一组 radio 必须共享
name="theme",否则无法互斥 - 想让“深色”选项选中时只改对应 label 背景,得写
input[value="dark"]:checked + label - 如果所有选项样式一致,用
input[name="theme"]:checked + label也行,但要注意:disabled 状态下:checked依然匹配,需额外加:not(:disabled) - 不要忘记
:focus-within或label:focus来支持键盘导航高亮
真正难的不是画个圆点或换背景色,而是让每个点击、每次 Tab、每种读屏模式都走对路径。结构错一丁点,:checked 就永远不触发;label 缺 padding,用户就点不中;忘了 not(:disabled),禁用项还会响应样式。这些细节不写进 CSS 规则里,光靠“看起来像”是没用的。


















