写过自定义 checkbox 的人大概都踩过这个坑——每个控件都要找一堆 hack
写过自定义 checkbox 的人大概都踩过这个坑——每个控件都是一场 hack:隐藏原生 input、用 label 模拟、用 ::before 画对勾、还要小心 keyboard focus 和 screen reader。最后引了一堆样式补丁,代码比业务逻辑还长。
CSS 其实从 2022 年开始就给了你一把钥匙,只是大部分人不知道它在哪。
这把钥匙叫 appearance: none。
它到底能干什么
appearance: none 的作用很简单:告诉浏览器「别给这个元素加任何原生样式」。按钮就是按钮,checkbox 就是 checkbox,语义还在,但渲染全归你管。
它能处理的控件覆盖了表单里最常见的几类:
input[type="checkbox"]、input[type="radio"]— 自定义选择控件select— 自定义下拉框(去掉浏览器默认箭头)button、input[type="submit"]— 统一按钮样式input[type="range"]— 自定义滑块input[type="file"]— 自定义文件上传按钮
三行代码,自定义 checkbox 长这样
这是最典型的用法。原生 checkbox 那个小方块在每个浏览器里长得都不一样——圆角、边框粗细、选中颜色,没有一个统一标准。appearance: none 让你从零开始画:
/* 先去掉原生样式 */
input[type="checkbox"] {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #d1d5db;
border-radius: 4px;
cursor: pointer;
/* 这里开始画你自己的样式 */
}
/* 选中状态 */
input[type="checkbox"]:checked {
background-color: #2563eb;
border-color: #2563eb;
}
/* 用 ::after 画对勾 */
input[type="checkbox"]:checked::after {
content: ;
display: block;
width: 5px;
height: 10px;
border: 2px solid white;
border-top: none;
border-left: none;
transform: rotate(45deg) translate(2px, -2px);
}
这个写法比传统的「隐藏原生 input,用 label 加背景图」干净得多——没有图片依赖,没有 opacity hack,CSS 全搞定。
select 也能这样自定义
select 的下拉箭头在 Chrome、Safari、Firefox 里长得完全不同,有时候还会被系统主题覆盖。用 appearance: none 去掉它,然后自己加箭头:
select {
appearance: none;
padding: 10px 36px 10px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
background-color: white;
background-image: url("data:image/svg+xml,%3Csvg xmlns=http://www.w3.org/2000/svg viewBox=0 0 20 20 fill=%236b7280%3E%3Cpath d=M5 8l5 5 5-5/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px;
cursor: pointer;
}
三件事:去掉默认箭头、加 SVG 小箭头、用 padding 留出箭头位置。就这样,跨浏览器一致了。
radio 按钮同理
input[type="radio"] {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #d1d5db;
border-radius: 50%;
cursor: pointer;
}
input[type="radio"]:checked {
border-color: #2563eb;
background-color: white;
}
/* 内部圆点 */
input[type="radio"]:checked::after {
content: ;
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #2563eb;
margin: 3px;
}
两个必须注意的坑
坑一:无障碍别丢了。 隐藏原生 input 的时候,别用 display: none 或 visibility: hidden,那会让屏幕阅读器读不到它。正确做法是用 position: absolute; opacity: 0; 把它移出文档流但保留语义。checkbox 本身的 :checked 状态在加了 appearance: none 之后依然有效,键盘 focus 也还在——只要你不手动把它 display: none,无障碍就不会断。
坑二:老 Safari 还要前缀。 iOS Safari 15.3 及之前、macOS Safari 15.4 及之前还需要 -webkit-appearance: none。生产环境建议这样写:
input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
/* 样式 */
}
写在前面的 appearance: none 是给现代浏览器看的,-webkit- 前缀兜底层兼容。
全球覆盖率 96%,现在用完全没问题
根据 caniuse 数据,appearance 属性(包含 none 值)全球覆盖率 96%+,Chrome 84+、Firefox 80+、Safari 15.4+、Edge 84+ 全部支持。2022 年进入 Baseline 之后,已经不需要任何 polyfill。
三步下一步
- 先试 checkbox。 在你的项目里找一个简单的表单,把
appearance: none用上去,感受一下「CSS 全权控制」是什么体验。不用任何 JS,不用任何图片依赖。 - 把 select 也做了。 加一个自定义箭头 SVG,跨浏览器统一下拉框外观,这是
appearance: none最实用的场景之一。 - 检查无障碍。 用 Tab 键验证键盘可聚焦,用屏幕阅读器验证读得出来。自定义样式不能以牺牲无障碍为代价——这是底线。
用了 appearance: none 之后,你可能会意识到之前靠多少 hack 才勉强实现了「自定义表单控件」。这把钥匙一直在,只是知道的人不多。
评论区
登录后可评论。