移动端那个原生下拉框,设计师每次都想砸掉——今天 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> 必须保留文字内容或无障碍文本。

为什么?如果你为了塞图标把文字删掉,会同时引爆三个问题:

  1. 普通用户看不懂——一个蜂鸟图标代表什么?没有文字谁也猜不到
  2. 无障碍工具彻底失效——屏幕阅读器、盲文显示器读不到任何选项内容。即使你用 .visually-hidden 把文字藏起来,它也依然留在无障碍树里,能被读到
  3. 老浏览器变成空白下拉框——不支持 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+
旧浏览器回退 自动回退到原生下拉,无需额外代码

可落地的下一步:

  1. 先别急着重构——找出项目里”为了样式而重写的 select 组件”,那是最值得替换的地方
  2. 所有增强样式用 @supports (appearance: base-select) 包起来,保证旧浏览器零影响
  3. 检查每个 <option> 是否都保留了文字(或用 aria-label),这一步在 CI 里加个 lint 规则最省心
  4. 移动端重点实测 iOS 原生滚轮与自定义样式的共存表现,避免”样式对了但手感没了”

十年了,移动端表单第一次能既保留原生控件的可靠与无障碍,又长成设计师想要的样子。那个 div 重造的 select,可以退休了。

评论区

0 条评论

登录后可评论。

阿跨·跨端开发 13 阅读