必须视觉隐藏原生控件并用伪元素重绘,因border/background被浏览器忽略;display:none会破坏可访问性与表单提交,应改用opacity:0+clip或clip-path保留dom存在性。

不能直接用 border 或 background 修改原生 <input type="checkbox"> 和 <input type="radio"> 的外观——浏览器会忽略这些声明。必须用视觉隐藏 + 伪元素重绘,再靠选择器联动状态。
为什么 display: none 不行?
把 input 设为 display: none 会让它彻底退出可访问性树和表单提交流程:键盘无法聚焦、屏幕阅读器读不到、form.submit() 时值丢失。这不是“隐藏”,是“销毁”。
- 正确做法是用
position: absolute; opacity: 0; clip: rect(0, 0, 0, 0);或clip-path: inset(100%);把它视觉移出但保留在 DOM 流中 - 必须保留
tabindex和focus响应能力,否则 WCAG 2.1 AA 级不达标 -
hidden属性同理无效——它等价于display: none
:checked + label 和 label > input:checked 的区别
两种结构影响选择器写法和 HTML 布局自由度:
-
input:checked + label:要求label紧跟在input后面,且同级。适合扁平结构,但改 DOM 顺序就失效 -
label > input:checked + span:把input包进label内(推荐),此时伪元素可直接作用于label子元素,HTML 更灵活,也天然支持点击label文本触发选中 - 别用
~(通用兄弟)代替+,除非你明确需要匹配后续所有同类元素——它性能略差,且易误触非目标节点
radio 圆形 vs checkbox 方形的实现关键
核心差异在伪元素的 border-radius 和内容绘制逻辑:
- radio:外框用
border-radius: 50%,选中态用::after绘制实心圆(content: ""+background或box-shadow) - checkbox:外框保持直角或小圆角,选中态优先用 SVG 或 Unicode 字符(如
"✓"),避免字体加载失败导致空白 - 两者都要处理
:indeterminate状态(仅 checkbox),JS 设置input.indeterminate = true后,CSS 可写input:indeterminate + .custom-icon::before显示横杠 - 别依赖
appearance: none——IE 完全不支持,Firefox 需-moz-appearance: none,且部分安卓 WebView 表现不稳定
focus 和可访问性容易漏掉的点
很多人只写 :checked,结果键盘用户 tab 进来时完全看不出焦点在哪:
- 必须加
input:focus + .custom-control::before { outline: 2px solid #007bff; outline-offset: 2px; } - 如果用 SVG 替代伪元素,要确保
svg有aria-hidden="true",而文字 label 保留语义 - 禁用状态(
input:disabled)需同步灰掉自定义图标和文字,且 pointer-events: none - 移动端 touch-area 至少 44×44px,所以
label的padding或min-width/min-height要够大
真正难的不是画个好看图标,而是让每个状态都可感知、可操作、可脚本化。一旦漏掉 :focus 或 :indeterminate,对残障用户就是不可用的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











