Highlight
Safari 27 和 Chrome 135 给原生
<select>元素新增了appearance: base-select和::picker(select)等伪元素,开发者用纯 CSS 就能完全自定义下拉菜单样式,<option>里还能放图片和 SVG,且自动保留键盘导航和屏幕阅读器支持。
核心内容
做网页表单的人,几乎都跟 <select> 下过功夫。设计师画了一个带图标、圆角、阴影的下拉菜单,前端拿到稿就头疼。原生 <select> 长什么样由操作系统说了算,macOS 上是蓝底白字的 pull-down button,Windows 上又是另一套面孔。想改?要么引入 Headless UI、Select2 这种几十 KB 的 JS 库,用一堆 <div> 模拟下拉行为,要么自己造轮子,手写键盘导航、焦点管理、ARIA 标签。代码写完了,还得拿 VoiceOver 和 NVDA 逐个测一遍。
现在这条路可以走通了。Safari 27 和 Chrome 135 同步上线 Customizable Select,核心思路是:不动 HTML 标签,只加 CSS。给 <select> 加一行 appearance: base-select,浏览器就解除对按钮和下拉面板的样式锁定,把控制权交还给开发者。<option> 里也能直接塞 <img>、<svg> 甚至 <video>。更关键的是,这些改动全建立在语义化 HTML 之上,键盘上下键、回车选中、屏幕阅读器朗读,全部原生支持,不需要额外代码。
Session 里演示了一个摄影师作品集网站。客户要在页面上加排序和分类筛选,用 <select> 做下拉菜单。原生控件放在页面上,跟网站的整体设计风格格格不入。用了 Customizable Select 后,按钮的背景、边框、圆角、内边距全部用 CSS 自定义,下拉箭头换成自定义 SVG,菜单展开时按钮颜色还能跟着变。整个改造只用了几行 CSS。
详细内容
开启自定义模式
(02:50)
第一步是给 <select> 加 appearance: base-select。这个属性告诉浏览器:不要用系统默认样式,改用一套最小化的基础样式,方便开发者覆盖。
body {
font-family: Gill Sans, sans-serif;
}
select {
appearance: base-select;
}
关键点:
appearance: base-select会继承父元素的字体设置,按钮上的文字自动跟页面其他文本对齐。- 这个属性不是 Safari 私有的,Chrome 135 也支持,不需要写前缀。
自定义按钮样式
(03:07)
基础样式开启后,就可以像写普通按钮一样写 CSS 了。
select {
appearance: base-select;
background-color: var(--green-10);
border: none;
padding: 0.6em 1em;
}
关键点:
background-color、border、padding、border-radius这些常规属性现在对<select>完全生效。- 不需要
!important,也不需要 hack 浏览器默认样式表。
替换下拉箭头
(03:08)
浏览器默认的下拉箭头可以用 ::picker-icon 伪元素替换。
select::picker-icon {
content: url(icons/arrow.svg);
width: 0.65em;
}
菜单展开时,按钮和箭头都能切换状态。用 :open 伪类实现:
select:open {
background-color: var(--green-100);
color: white;
}
select:open::picker-icon {
content: url(icons/arrow-white.svg);
}
关键点:
::picker-icon是 Customizable Select 新增的伪元素,专门用于控制下拉箭头。:open伪类在菜单展开时匹配,可以省掉用 JS 监听focus/blur的麻烦。
自定义下拉面板
(04:08)
下拉菜单本身也能自定义。用 ::picker(select) 选中面板,同样要加 appearance: base-select 才能彻底解除系统样式。
::picker(select) {
appearance: base-select;
padding: 4px;
margin-top: 0.5em;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 9px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
关键点:
::picker(select)里的appearance: base-select必须写,否则面板还是系统默认的白底黑边。margin-top可以控制面板和按钮之间的间距。
自定义选项样式
(04:36)
选项的选中状态和默认勾选标记也能改。
option:checked {
font-weight: 600;
}
option:not(:checked) {
color: #777;
}
option::checkmark {
content: url(checkmark.svg);
width: 0.65em;
}
关键点:
option:checked选中当前已选中的选项,option:not(:checked)选中其他选项。::checkmark伪元素控制选中项前面的勾选标记,可以用content属性替换成任意 SVG。
在选项中放图片
(05:31)
Customizable Select 允许在 <option> 里放富内容,比如图片和标签。
<option value="flower">
<img src="flowers.svg" alt="">
<span class="text">Flowers</span>
</option>
关键点:
<option>内部现在可以包含任意 HTML 元素。alt=""必须写,否则屏幕阅读器会把图片描述和 “Flowers” 文本各读一遍。
去掉默认勾选标记,改用背景色高亮选中项:
option::checkmark {
display: none;
}
option:checked {
background: #00857e;
color: white;
}
用 Grid 布局排列选项
(06:20)
选项多了,单列下拉会变得很长。::picker(select) 支持 display: grid,直接把下拉面板改成网格布局。
::picker(select) {
display: grid;
grid-template: 1fr 1fr / 1fr 1fr 1fr;
gap: 1rem;
}
关键点:
grid-template: 1fr 1fr / 1fr 1fr 1fr表示 2 行 3 列。- 这个 Grid 布局只作用于下拉面板内部,不影响页面其他元素。
在按钮上显示选中项的富内容
(07:11)
原生 <select> 的按钮默认只显示纯文本。如果 <option> 里有图片,选中后按钮上不会显示图片。要解决这个问题,需要在 <select> 里放一个自定义 <button>,并在其中插入 <selectedcontent> 元素。
<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option value="anywhere">
<img src="icons/all.svg" alt="">
<span class="text">Everything</span>
</option>
<option value="buildings">
<img src="icons/buildings.svg" alt="">
<span class="text">Buildings</span>
</option>
<option value="flowers">
<img src="icons/flower.svg" alt="">
<span class="text">Flowers</span>
</option>
</select>
关键点:
<button>必须是<select>的第一个子元素。<selectedcontent>是一个空标签,浏览器会自动把当前选中<option>的 DOM 结构克隆进去。- 以前 HTML 不允许
<button>放在<select>里,现在 Customizable Select 放宽了这个限制。
渐进增强
(07:53)
在不支持 Customizable Select 的浏览器里,<select> 会降级为系统原生下拉菜单。用户看到的还是熟悉的平台控件,键盘导航和屏幕阅读器功能完全保留。因为底层用的是语义化 HTML,不需要写任何 fallback 代码。
核心启发
-
做一个带图标的国家/地区选择器:在
<option>里放国旗 SVG,用<selectedcontent>让选中后按钮上也显示国旗。入口是appearance: base-select+::picker(select)。 -
做一个颜色选择器:每个
<option>放一个色块<div>,下拉面板用display: grid排成 4x4 的色板。Session 末尾演示了一个径向颜色选择器,原理相同。入口是grid-template布局 + 富内容<option>。 -
做一个分类筛选组件:电商网站常见的”按类别筛选”可以用 Customizable Select 实现,每个选项带分类图标和名称,选中后按钮同步显示图标。入口是
<selectedcontent>+ 自定义::picker-icon。 -
替换项目里的 JS Select 库:把用 React-Select、Headless UI 实现的纯展示型下拉菜单换回原生
<select>,能直接砍掉依赖包和对应的 ARIA 维护成本。入口是全局搜索项目里的import { Select },评估哪些可以替换。 -
做一个图片尺寸选择器:摄影或设计工具里常见的”按尺寸筛选”(缩略图/中等/原图),每个选项放预览图和尺寸文字,网格排列。入口是
display: grid+<img>in<option>。
关联 Session
- Learn CSS Grid Lanes — Session 里的摄影师网站示例用 Grid Lanes 做图片瀑布流布局,跟 Customizable Select 配合使用效果最佳。
- What’s new in WebKit — 了解 Safari 27 的其他 Web 平台新特性,包括 CSS 和 JavaScript 的更新。
- Design principles for spatial user interfaces — 如果要把自定义下拉菜单的设计语言延伸到空间计算界面,可以参考 Apple 的空间 UI 设计原则。
- What’s new in Safari and Web Apps — 了解 Safari 在沉浸式 Web 和 PWA 方面的最新进展。
评论
GitHub Issues · utterances