可定制 select(appearance: base-select、::picker(select))解决什么问题?
原生 select 的下拉面板样式由操作系统决定,几乎改不了,所以很多项目只能自己写下拉组件。给 select 和 ::picker(select) 设置 appearance: base-select 后,可以完全自定义按钮和下拉面板,选项里还能放图标,同时保留原生 select 的键盘操作、表单提交和无障碍语义。不支持的浏览器会退回普通 select。
简答
原生
<select>展开后的下拉面板由操作系统绘制,CSS 基本改不了它的样式,选项里也只能放纯文字。设计稿一要求带图标、圆角、自定义箭头,很多团队就只好用div自己写一个下拉组件,键盘操作和无障碍都得重新实现。可定制 select 让原生
<select>也能完全改样式。在select和代表下拉面板的::picker(select)上都设置appearance: base-select,浏览器就去掉系统样式,只保留一套最基础的样式,剩下的都交给 CSS。设置以后,
<select>的第一个子元素可以是<button>,用来替换关闭状态下的按钮,里面放<selectedcontent>显示当前选中项。<option>里可以放图片、图标这类非交互内容。::picker-icon改下拉箭头,::checkmark改选中标记。下拉面板本身是一个 popover,打开时进入顶层,并且已经自动用锚点定位跟按钮关联好了。要注意支持现状。按 MDN 兼容数据,Chrome 135 和 Safari 27 起支持,Firefox 只能通过实验开关开启,所以还不是 Baseline。好在它是渐进增强,不支持的浏览器会忽略这些写法,显示普通的
<select>,功能照常可用。
怎样开启
开启只需要一段 CSS,两个地方都要写:
select,
::picker(select) {
appearance: base-select;
}
select 设置后,关闭状态的按钮可以自定义;::picker(select) 设置后,展开的下拉面板可以自定义。可以只给 select 开启、面板保持系统样式,但不能只开启面板而不开启 select。
选项里放图标
下面做一个带图标的选择框:
<select name="pet">
<button>
<selectedcontent></selectedcontent>
</button>
<option value="">请选择宠物</option>
<option value="cat">
<span class="icon" aria-hidden="true">🐱</span>
<span>猫</span>
</option>
<option value="dog">
<span class="icon" aria-hidden="true">🐶</span>
<span>狗</span>
</option>
</select>
以前 <select> 里不能放 <button>,<option> 里也只能放文字,这两点在可定制 select 里都放开了。
<selectedcontent> 会把当前选中的 <option> 的内容克隆一份显示在按钮里,所以选了“猫”,按钮上也会带着猫的图标。这个按钮默认是 inert 的,即使里面放了链接或按钮,整体也只当成一个按钮来交互。
那表单提交的值是什么?和原来一样是 value。没有写 value 时,浏览器取选中项的 textContent 去掉首尾空白作为值。
箭头、选中标记和面板位置
select::picker-icon {
transition: rotate 0.4s;
}
select:open::picker-icon {
rotate: 180deg;
}
option::checkmark {
content: '✓';
}
::picker(select) {
top: calc(anchor(bottom) + 4px);
}
::picker-icon 是按钮里的下拉箭头,配合 :open 伪类可以在展开时转过来。::checkmark 是选中项前面的勾。这两个伪元素不在无障碍树里,content 写的文字读屏软件不会读出来,所以不要把重要信息放进去。
下拉面板是一个 popover,打开时进入顶层,不会被父元素的 overflow 裁掉。它和按钮之间已经有隐式的锚点关联,直接用 anchor() 就能调整位置,不用再写 anchor-name。浏览器默认样式还带了备选位置,面板快超出视口时会自动换个方向。锚点定位的细节见《CSS Anchor Positioning 适合解决哪些浮层布局问题?》。
不支持的浏览器会怎样
不支持的浏览器会完全忽略 <button><selectedcontent></selectedcontent></button> 这段结构,<option> 里的非文字内容也会被去掉,只留下文字。最后得到的是一个普通的原生 <select>,能正常选择和提交。
所以它可以直接用在生产环境里,只要保证每个选项的文字本身就能说清楚意思。需要只在支持的浏览器里生效的样式,可以放进 @supports (appearance: base-select) 里。MDN 还提醒,有些 JavaScript 框架会拦截这些新元素,或者在服务端渲染时导致水合失败,用之前要在自己的框架里试一下。
常见追问
和自己用 div 写的下拉组件比,好在哪里?
它仍然是原生
<select>,键盘操作、表单提交和读屏语义都由浏览器提供,不用自己实现。自己写的组件要补齐这些,见《可访问的 Combobox 需要哪些键盘和 ARIA 行为?》。
下拉面板能做打开动画吗?
可以。面板是 popover,可以和其他 popover 一样用
@starting-style做进入动画,见《@starting-style 和离散属性过渡解决了什么问题?》。