配了五年 select,每次想改样式都要接一个 30KB 的库——今天 8 行 CSS 把这件事彻底原生化了

配了五年 select,每次想改样式都要接一个 30KB 的库——今天 8 行 CSS 把这件事彻底原生化了

写 select 样式这件事,前端开发者踩了快十年的坑。native <select> 浏览器原生渲染,OS 级别接管,CSS 能改的只有 font-size、color、border 这几个边角料。想要圆角、图标、自定义下拉面板?接 Select2。想要搜索过滤?接 Choices.js。想要好看点的主题?继续加 CSS,加几十行。

结果:一个表单下拉框,包体积轻轻松松 30KB 起步,accessibility 还要自己维护。

Chrome 134 开始,这件事被一个 CSS 属性彻底原生化了。


8 行 CSS,替代 45 行 + 一个 npm 包

核心就两行:

select,
select::picker(select) {
  appearance: base-select;
}

加上基础样式和选中态:

select {
  border: 2px solid #dddddd;
  background: #eeeeee;
  padding: 10px;
  transition: 0.4s;
}
select:hover,
select:focus {
  background: #dddddd;
}

加起来 8 行。没有 JS import,没有 npm install,没有 .choices__inner.choices__list.choices__item 一堆选择器要覆盖。

对比一下 Select2 的引入成本:

// 旧:npm install choices.js + 30KB bundle
import Choices from 'choices.js';
new Choices('#my-select', { searchEnabled: false });
// + 30 行 CSS

打开了什么?

appearance: base-select 是一把开关,打开之后浏览器会暴露 select 的内部结构供 CSS 操控:

::picker(select) — 下拉面板本身。可以加圆角、阴影、内边距,可以加 transition 动画:

select::picker(select) {
  border-radius: 12px;
  border: 1px solid #e0e0e0;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  transition: scale 0.2s;
}

::picker-icon — 下拉箭头。可以换颜色、换 SVG、加旋转动画:

select::picker-icon {
  color: #999999;
  transition: 0.4s rotate;
}
select:open::picker-icon {
  rotate: 180deg;
}

::checkmark — 选中项的对勾。可以用 emoji 替换,或者直接隐藏:

select option:checked {
  background: var(--accent);
}

<selectedcontent> 元素 — 可以放在 <select> 内部,让关闭状态下的按钮显示富文本内容,不只是纯文字了。

Anchor positioning 自动生效 — 下拉面板会自动贴住触发按钮,超出视口会自动翻转到上方,不需要你写任何位置计算。


解决了哪些老问题

overflow 穿透。以前 Select2 的下拉经常被父容器 overflow: hidden 裁掉,要套各种 hack。现在下拉面板渲染在 top layer,不受父容器约束。

移动端行为。以前用 Select2 在 iOS 上表现往往和原生控件打架——键盘弹起时机不对,滚动行为异常。base-select 完全走浏览器原生路径,移动端行为和原生 select 完全一致。

无障碍。Select2 要自己做 ARIA 属性维护、focus 管理、键盘导航。base-select 的键盘导航(方向键选择、回车确认、ESC 关闭)浏览器全包,screen reader 支持是白来的。


局限在哪里

目前只有 Chromium 支持,Chrome 134+、Edge 134+。Firefox 和 Safari 还没上,global coverage 约 96%,还没到 Baseline。

所以渐进增强写法是标配:

@supports (appearance: base-select) {
  select,
  select::picker(select) {
    appearance: base-select;
  }
  /* 定制样式放这里 */
}

不支持的浏览器会忽略这段 CSS,回退到原生 select——功能完全可用,只是样式朴素一点。

搜索功能仍然需要 JS,base-select 原生不提供 typeahead。如果你的场景里 select 选项超过 20 个,用户需要搜索筛选,还是要考虑自定义组件。


三步落地

第一步:升级 Chrome/Edge 测试

在 Chrome 134+ 打开 MDN 的 customizable select 示例,确认环境正常。

第二步:加 @supports 渐进增强

把 base-select 相关样式包在 @supports (appearance: base-select) 里,不支持的浏览器完全不受影响。

第三步:逐步替换现有 Select2 实例

从非关键的下拉框开始,保留 JS 库作为 fallback,等稳定了再推进到全站。


配了五年 select,每次想改样式都要写一堆 CSS override 还要接一个 30KB 的库——今天这件事,被一个 appearance: base-select 彻底翻篇了。不是实验性功能,不是 polyfill,是浏览器平台的原生能力。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 62 阅读