移动端那个原生下拉框,设计师每次都想砸掉——今天 Safari 27 和 Chrome 135 让 select 终于能自己改样式了
移动端那个原生下拉框,设计师每次都想砸掉——今天 Safari 27 和 Chrome 135 让 select 终于能自己改样式了。
做了十年移动端表单,<select> 一直是那个”能用但没法看”的控件:iOS 上弹出滚轮,安卓上弹出系统菜单,颜色、圆角、图标统统改不了。过去唯一的解法是——把 select 扔掉,用 div + JS 重新实现一个。代价是键盘导航、屏幕阅读器、移动端原生滚轮体验全部自己擦屁股。
Safari 27(9 月 17 日发布)和 Chrome 135 现在支持 Customizable Select:一行 appearance: base-select 就能沿用原生控件,同时把外观完全交给 CSS。
之前的做法:div 重造 + 一堆补丁
<!-- 旧方案:假装是个 select -->
<div class="select" tabindex="0" role="listbox">
<span class="select-value">全部类别</span>
<ul class="select-menu" hidden>
<li role="option">建筑</li>
<li role="option">花卉</li>
</ul>
</div>
这段代码要自己处理:Tab 键聚焦、方向键选择、Esc 关闭、点击外部收起、屏幕阅读器朗读 aria-activedescendant……任何一处漏了,无障碍就废了。而且移动端你再也拿不到系统原生的选择滚轮。
之后的做法:原生 select,随便样式化
/* 1. 触发新的样式模型 */
select {
appearance: base-select;
font-family: inherit;
color: #333;
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px 14px;
}
/* 2. 自定义下拉箭头 */
select::picker-icon {
content: "▼";
color: #888;
}
/* 3. 菜单展开时改变按钮颜色 */
select:open {
border-color: #4f46e5;
}
/* 4. 让弹出的选项菜单也能被样式化 */
select::picker(select) {
appearance: base-select;
border: 1px solid #e5e7eb;
box-shadow: 0 8px 24px rgba(0,0,0,.12);
border-radius: 10px;
}
/* 5. 选中项高亮 + 替换默认勾选标记 */
option:checked { background: #eef2ff; }
option::checkmark { content: "✓"; color: #4f46e5; }
更关键的是,选项里现在可以塞真正的 HTML:
<select>
<option value="all">
<img src="icons/all.svg" alt=""> 全部
</option>
<option value="building">
<img src="icons/building.svg" alt=""> 建筑
</option>
</select>
图片、说明文字、色块都能进选项。你甚至能用 Grid / Flexbox 把选项排成网格,让它看起来完全不像一个传统下拉菜单,但底层还是语义化的 <select>。
还有一个新元素 <selectedcontent>,放在 <button>(作为 select 的第一个子元素)里,把当前选中项的富内容镜像到按钮上——这样按钮里也能显示选中的图标和文字。
别踩这个坑:WebKit 官方的那条”黄金规则”
WebKit 团队专门发了一篇博客《The golden rule of Customizable Select》,反复强调一件事:
每个
<option>必须保留文字内容或无障碍文本。
为什么?如果你为了塞图标把文字删掉,会同时引爆三个问题:
- 普通用户看不懂——一个蜂鸟图标代表什么?没有文字谁也猜不到
- 无障碍工具彻底失效——屏幕阅读器、盲文显示器读不到任何选项内容。即使你用
.visually-hidden把文字藏起来,它也依然留在无障碍树里,能被读到 - 老浏览器变成空白下拉框——不支持
base-select的浏览器会回退到原生控件,此时里面全是空选项
正确姿势是渐进增强:
@supports (appearance: base-select) {
/* 只有在支持新特性时才做样式和图标增强 */
select::picker-icon { content: "▼"; }
}
图标是增强,不是替代。删掉文字换图标,是倒退不是进步。
支持情况与迁移建议
| 能力 | 支持情况 |
|---|---|
appearance: base-select |
Safari 27、Chrome 135+ |
::picker(select) / ::picker-icon / ::checkmark |
Safari 27、Chrome 135+ |
选项内嵌 HTML、<selectedcontent> |
Safari 27、Chrome 135+ |
| 旧浏览器回退 | 自动回退到原生下拉,无需额外代码 |
可落地的下一步:
- 先别急着重构——找出项目里”为了样式而重写的 select 组件”,那是最值得替换的地方
- 所有增强样式用
@supports (appearance: base-select)包起来,保证旧浏览器零影响 - 检查每个
<option>是否都保留了文字(或用aria-label),这一步在 CI 里加个 lint 规则最省心 - 移动端重点实测 iOS 原生滚轮与自定义样式的共存表现,避免”样式对了但手感没了”
十年了,移动端表单第一次能既保留原生控件的可靠与无障碍,又长成设计师想要的样子。那个 div 重造的 select,可以退休了。
评论区
登录后可评论。