CSS 属性 appearance: base-select 能够像常规 HTML 元素一样设置 <select> 元素及其选择器(通过 ::picker(select))的样式。
结合使用 Grid 或 Flexbox 等现代 CSS 布局技术,您可以摆脱传统的堆叠式垂直列表,在不使用自定义 JS 的情况下打造丰富的视觉体验。
例如,您可以使用 display: grid 将选项设置为二维卡片网格。
这非常适合颜色选择器、表情符号选择器或商品款式/规格,在这些场景中,丰富的视觉菜单比列表更有效。
如何实现
1. 启用基本样式
将 appearance: base-select 同时应用于 <select> 元素及其内部选择器伪元素 select::picker(select):
select.grid-picker {
&, &::picker(select) {
appearance: base-select;
}
}
2. 设置选择器容器的样式
定位 select:open::picker(select) 并应用 display: grid(或 display: flex)。
像设置任何容器一样定义列和间隙。
select.grid-picker:open::picker(select) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
重要提示::open是必需的。如果您设置 select::picker(select) 的样式,即使选择器处于关闭状态,选择器也会显示!
3. 设置选项样式
定位到 <option> 元素,将其样式设置为网格项或卡片。
您可以在其中添加图片、SVG 或复杂布局。
/* Style options as grid cards */
.grid-picker option {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* padding, border-radius, background, etc. */
}
4. 设置所选选项的样式
如需为所选 option 设置样式,请使用 option:checked。例如:
.grid-picker option:checked {
position: relative;
border: 2px solid var(--primary-color);
background-color: oklab(from var(--primary-color) l a b / 8%);
}
与任何界面控件一样,请勿仅通过颜色传达状态,而应使用多种视觉指示标志(例如颜色、边框粗细、对勾标记)来传达所选选项。
5. (可选)为所选选项的对勾标记设置样式
默认情况下,浏览器会将对勾标记呈现为每个 <option> 的第一个子项。
它的行为类似于带有 content: "✓" / "" 的 ::before,在作者提供的 ::before(如果有)之前呈现。
如果未选择该选项,则会通过 visibility: hidden 隐藏对勾标记。
您可以使用 ::checkmark 伪元素为其设置样式,也可以将其完全隐藏(如果您想仅通过样式传达选择状态或使用自定义指示器)。
例如,如需将其放置在选项的右上角,您可以执行以下操作:
select.grid-picker {
> option::checkmark {
/* Hide the checkmark for unselected options */
display: none;
}
> option:checked {
position: relative;
&::checkmark {
position: absolute;
inset-block-start: -.4em;
inset-inline-end: -.5em;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-inline-size: 1.6em;
aspect-ratio: 1;
padding-right: 0.12em;
padding-left: 0.05em;
border-radius: 50%;
background: var(--primary-color);
color: white;
font-size: 70%;
font-weight: bold;
}
}
}
提示:在对勾标记周围绘制容器(例如圆形)时,请将其稍微向上移动一点(就像我们在这里使用 padding-bottom 和 padding-left 所做的那样),以便根据其形状进行视觉居中。
请勿为此使用 padding-inline-start 或 padding-block-end,因为它们会根据书写模式而变化,而对勾标记的形状不会变化。
6. (可选)自定义所选选项的显示
默认情况下,<select> 将显示所选选项的纯文本版本。
如需为所选值显示富媒体内容,请在 <button> 中使用 <selectedcontent>,和/或以不同方式显示所选选项(例如,隐藏某些详细信息)。
请勿在不使用 <button> 的情况下使用 <selectedcontent>,因为 <selectedcontent> 会像任何其他内容元素一样,直接成为选择器的一部分。
最佳做法
- 测试布局行为。设置
appearance: base-select会移除默认浏览器行为,即根据最长选项宽度调整选择框大小。您可能需要设置固定宽度或使用 flex/grid 限制来防止布局偏移。 - 确保您的
<select>具有name属性和关联的<label>。这样可以确保即使采用自定义界面,屏幕阅读器也能访问该组件,并且该组件能与标准表单提交功能正常配合使用。 - 在选项中使用标准
value属性,以确保表单提交功能与之前完全相同。
无障碍
原生 <select> 仅处理线性向上/向下箭头导航,因此在打开的选择器中,向左/向右键不起作用,而向上/向下键仍会按顺序浏览各项,无论行和列如何。
如需实现遵循网格的空间导航,您可以拦截 keydown,并在用户使用箭头键时将焦点路由到所按方向上最近的选项(例如使用 getBoundingClientRect())。
如需确定合适的下一个/上一个项,请使用对实际几何图形进行操作的 DOM 方法(例如 getBoundingClientRect()),而不是解析 grid-template-columns 或 grid-column 等 CSS 字符串,因为对于复杂的网格、子网格、RTL 和其他布局场景,这些字符串可能很脆弱且容易出错。
也就是说,不要进行不必要的过度设计;请使用您知道的关于布局的不变量来简化此逻辑。
注意:这仅涉及键盘导航。<select> 及其 ::picker() 仍具有其默认角色(menu 或 listbox),这些角色无法被替换,也不允许包含具有 role="grid" 的元素。
在选择器中嵌套一个带有 role="grid" 的封装容器在实践中可行,但并不有效,并且可能会让 AT 用户感到意外。
在某些情况下,使用 <optgroup> 对选项进行分组可能会在不使用 role="grid" 的情况下产生良好的屏幕阅读器用户体验。
否则,最终可能需要改用自定义解决方案,即使用 popover API、锚点定位和 role="grid",为屏幕阅读器用户提供理想的用户体验。
后备策略
可自定义 <select> 的回退和浏览器支持
对于尚不支持 appearance: base-select 的浏览器,<select> 元素会优雅降级为标准操作系统下拉菜单。
- 忽略非文本内容:旧版浏览器会剥离
<option>标记内的 HTML 标记(例如<svg>或<div>),仅呈现文本节点。确保<option>的文本内容本身清晰易读且有意义。 - HTML 结构处理:标准解析器可能会忽略
<select>内的<button>和<selectedcontent>标记,或将其视为无效。如果您将标准文本视为可读的后备内容,则渐进增强功能不需要任何繁重的 JavaScript polyfill。
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});