WWDC Quick Look 💓 By SwiftGGTeam
Rediscover the HTML select element

Rediscover the HTML select element

观看原视频

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-colorborderpaddingborder-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

评论

GitHub Issues · utterances