多年来,<select>
元素一直很难被美化。开发者们要么接受浏览器的默认样式,要么不得不使用大量的JavaScript来解决。为什么这种情况已经持续这么久了?
<select>
不能设置样式?
<select>
组件是一个表单元素,这意味着浏览器原生处理了许多行为,如下拉菜单逻辑(你有没有注意到选项列表可能会超出窗口范围?)、键盘导航和辅助功能。然而,由于这些控件与操作系统紧密结合,样式设计受到了很大的限制。
因为没有现成的原生样式可用,开发人员只好转向使用库。
- jQuery UI(早期2010年代):将
<select>
包裹在一个div
中,并用<ul>
替换它。 - 自定义下拉选择(2015–2022):React/Vue 解决方案通常完全替换
<select>
。 - shadcn/ui(现代的做法):利用 Radix UI 创建可访问的下拉选择。
虽然这些解决方案可行,但它们也带来了取舍:多余的JavaScript,可能的无障碍问题以及性能损耗。
进入base-select
:一个新的方法,但有一个注意事项
随着引入了新的 CSS 属性 base-select
,浏览器将允许对 <select>
元素进行完整的 CSS 样式设置,而不覆盖大多数内置功能。这意味着,用户可以进行完整的 CSS 样式设置,而不会影响大多数默认功能。
- 无需使用 JavaScript 来实现下拉菜单。
- 完全掌控外观,同时保持内置的易用性。
- 可能更快渲染,性能更佳。
重要提示: base-select
属性目前还在试验中,仅在 Chrome 134+ 中可用。请注意在实际应用中谨慎使用。
用 base-select
风格美化 <select>
当对下拉列表框进行样式设置时,你必须这样为select
和select::picker(select)
添加appearance: base-select;
。
选择器将应用基础选择器的外观,当选择器作为拾取器时,也将应用基础选择器的外观。
全屏模式,退出全屏
这告诉浏览器允许 <select>
元素的样式用 CSS 来样式化,而不是使用系统默认的外观。
理解 ::picker(select)
: 此伪类表示 <select>
元素的下拉框(即“picker”)。它允许你单独对下拉框进行样式设置。
有一些可用的伪类和伪元素可以用于修改select
元素。下面列了一些有用的伪类和伪元素,select元素
select {
appearance: base-select;
/* 定义 'button' 样式 */
&::picker(select) {
appearance: base-select;
/* 定义 'listbox' 样式 */
}
&::picker-icon {
/* 设置图标样式 */
}
&:not(:open) {
/* 关闭时的样式设置 */
&::picker(select) {
/* 关闭时的 'listbox' 样式设置 */
}
}
&:open {
/* 打开时的样式设置 */
&::picker(select) {
/* 打开时的 'listbox' 样式设置 */
}
}
& option {
/* 选项样式设置 */
&::checkmark {
/* 选中时对勾样式设置 */
}
&:checked {
/* 选中时选项样式设置 */
}
}
}
进入全屏,退出全屏
Demo: 仅使用 CSS 的 shadcn/ui Select:让我们只用 CSS 重新实现 shadcn/ui 的 select 组件。下面是如何实现的:
- 使
<select>
的样式与 shadcn/ui 的外观一致。 - 自定义下拉列表的外观。
- 确保无障碍功能完好无损。
注意: 这个演示仅适用于 Chrome 134+ 及以上版本。如果你无法进行测试,我已经在下面附上了一个 GIF 动图,你可以通过 GIF 动图了解它的运行情况。
标记语言
HTML结构仍然保持简单。<selectedcontent>
元素允许我们单独显示和样式化选定的选项,并通过添加一个 Chevron 图标来模仿 shadcn/ui 的样式。
<select>
<button>
<selectedcontent></selectedcontent>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<optgroup>
<label>水果</label>
<option value="" hidden disabled selected>请选择一个水果选项</option>
<option>苹果</option>
<option>香蕉</option>
<option>蓝莓(莓果)</option>
<option>葡萄</option>
<option>菠萝</option>
</optgroup>
</select>
切换到全屏模式, 点击退出全屏
设置选择按钮的外观
首先,我们得用这个属性 appearance: base-select
并设置基本样式。
select {
appearance: 基础选择器;
color: #71717a;
background-color: 透明;
width: 180px;
box-sizing: border-box;
padding: 0.5rem 0.75rem;
border: 1px solid #e4e4e7;
border-radius: calc(0.5rem - 2px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
cursor: pointer;
}
进入全屏 退出全屏
调整内容和图标的摆放位置:
select > button {
display: flex;
width: 100%;
font-family: inherit;
color: currentColor;
}
select > button > svg {
margin: 0 0 0 auto;
width: 1.2rem;
height: 1.2rem;
}
全屏,退出全屏
下拉列表框的样式
listbox
需要单独进行样式设置,以确保其在打开时能够平滑显示。我们采用较新的starting-style
,以便从display: none
开始进行动画效果。
select::picker(select) {
appearance: base-select;
border: 1px solid #e4e4e7;
padding: 0.25rem;
margin-top: 0.25rem;
border-radius: calc(0.5rem - 2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -2px rgba(0, 0, 0, 0.1);
cursor: default;
transition: opacity 225ms ease-in-out, transform 225ms ease-in-out;
transform-origin: top;
transform: translateY(0);
opacity: 1;
@starting-style {
transform: translateY(-0.25rem) scale(0.95);
opacity: 0;
}
}
点击全屏进入,点击退出全屏
增强可访问性和互动性
提高焦点的可见度,并确保占位文本更加显眼。
select:focus-visible {
outline: 2px solid #a1a1aa;
outline-offset: -1px;
}
select:has(option:not([hidden]):checked) {
color: #18181b;
}
全屏模式 退出全屏
自定义复选标记
我们可以把默认的对勾替换成自定义的 SVG 图标。
select option::after {
content: "";
width: 1rem;
height: 1.5rem;
margin-left: auto;
opacity: 0;
background: center / contain no-repeat
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2318181b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'%3E%3C/path%3E%3C/svg%3E");
}
select option:checked::after {
opacity: 1;
}
切换到全屏 退出全屏
结论部分<select>
组件终于变得更加灵活。我们可以用 base-select
创建漂亮的下拉菜单,无需使用 JavaScript。想了解更多详情,请参阅 open ui 介绍。
你对 base-select 有什么看法?是不是有点等不及要告别那些重依赖 JavaScript 的下拉菜单了?base-select(这里可以加入注释说明 base-select 的具体含义或功能)。