Selectmenu Element (Explainer)
<selectmenu> element explainer
Background
The <select>
element does not provide enough customization for web developers, which leads them to implement their own. These implementations can lead to reduced performance, reliability, and accessibility compared to the native form control elements. More on that is in the select proposal and Custom Control UI.
Testing out the <selectmenu> element
<selectmenu>
is currently implemented behind a flag in Chromium. To use it, enable the Experimental Web Platform features flag in about:flags.
If you encouter bugs or limitations with the design of the control, please send your feedback by creating issues on the open-ui GitHub repository. Here is a list of open selectmenu bugs in open-ui.
Anatomy of the selectmenu
Because the various parts of the selectmenu can be styled, it’s important to understand the anatomy of this UI control.
<select>
- The root element that contains the button and listbox.<button>
- The button element that triggers the visibility of the listbox.<selected-value>
- The element that displays the value of the currently selected option (optional). Note that this part does not necessarily have to be places inside the<button>
part (read on for more information about how to slot your own markup).<marker>
- Something that indicates this button opens a listbox, e.g. an icon.<listbox>
- The wrapper that contains the<option>
(s) and<optgroup>
(s).<optgroup>
- Groups<option>
(s) together with a label (optional).<option>
- Can have one or more and represents the potential values that can be chosen by the user.
Default behavior
The default behavior of the <selectmenu>
control mimics the behavior of the <select>
control. You can use it just like a native <select>
, with the following minimal markup:
<selectmenu>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
When doing so, the default button, select-value, and listbox are created for you.
Styling parts of the control
One way to style the control to match your requirements is to use the CSS ::part()
pseudo-element to select the different parts within the control’s anatomy that you wish to style.
<style>
.my-select-menu::part(button) {
color: white;
background-color: #f00;
padding: 5px;
border-radius: 5px;
}
.my-select-menu::part(listbox) {
padding: 10px;
margin-top: 5px;
border: 1px solid red;
border-radius: 5px;
}
</style>
<selectmenu class="my-select-menu">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
The above example results in the following style:
::part()
can be used to style the button
, selected-value
, and listbox
parts of the control.
Use your own markup
You can further customize the control to fit your needs by providing your own markup to replace the default one, and extend and re-order the parts.
Slotting your own content
A <selectmenu>
has named slots that can be referenced to replace the default parts.
For example, to replace the default button with your own, you can do the following:
<style>
.my-custom-select [slot='button'] {
display: flex;
align-content: center;
}
.my-custom-select button {
padding: 5px;
border: none;
background: #f06;
border-radius: 5px 0 0 5px;
color: white;
font-weight: bold;
}
.my-custom-select .label {
padding: 5px;
border: 1px solid #f06;
border-radius: 0 5px 5px 0;
}
</style>
<selectmenu class="my-custom-select">
<div slot="button">
<button behavior="button">Open</button>
<span class="label">Choose an option</span>
</div>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</selectmenu>
The slot="button"
attribute on the outer <div>
tells the <selectmenu>
to replace its default button with the contents of that <div>
.
The behavior="button"
attribute on the inner <button>
tells the browser that this element is what we want to use as the new button. The browser will automatically apply all the click and keyboard handling behavior to this element as well as the appropriate accessibility semantics.
The above code snippet results in the following style:
Note that the slot
and behavior
attributes can be used on the same element too.
You can replace the default listbox part in a similar fashion:
<style>
.my-custom-select [popup] {
width: 300px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 10px;
padding: 10px;
box-shadow: none;
margin: 10px 0;
border: 1px solid;
background: #f7f7f7;
}
</style>
<selectmenu class="my-custom-select">
<div slot="listbox">
<div popup behavior="listbox">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</div>
</div>
</selectmenu>
The popup
attribute used in this example’s <div popup>
is from the proposal at Popup API (Explainer). The element with behavior="listbox"
is required to be a <div popup=popup>
. Applying behavior="listbox"
tells the browser to apply select menu listbox behavior to that element: it will be opened when the <selectmenu>
’s button is clicked, and the user can select <option>
s inside it with mouse, arrow keys, and touch.
The above code snippet results in the following style:
Extending the markup
Not only can you replace the default parts with your own, you can also extend the control’s markup by adding new elements. This can be useful to augment the listbox or button with extra information, or to add new functionality.
Consider the following example:
<style>
.my-custom-select [slot='button'] {
display: flex;
align-items: center;
gap: 1rem;
}
.my-custom-select button {
border: none;
margin: 0;
padding: 0;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: grid;
place-content: center;
}
.my-custom-select button::before {
content: '\25BC';
}
.my-custom-select [popup] {
padding: 0;
}
.my-custom-select .section {
padding: 1rem 0 0;
background: radial-gradient(ellipse 60% 50px at center top, #000a 0%, transparent 130%);
}
.my-custom-select h3 {
margin: 0 0 1rem 0;
text-align: center;
color: white;
}
.my-custom-select option {
text-align: center;
padding: 0.5rem;
}
</style>
<selectmenu class="my-custom-select">
<div slot="button">
<span class="label">Choose a plant</span>
<span behavior="selected-value" slot="selected-value"></span>
<button behavior="button"></button>
</div>
<div slot="listbox">
<div popup behavior="listbox">
<div class="section">
<h3>Flowers</h3>
<option>Rose</option>
<option>Lily</option>
<option>Orchid</option>
<option>Tulip</option>
</div>
<div class="section">
<h3>Trees</h3>
<option>Weeping willow</option>
<option>Dragon tree</option>
<option>Giant sequoia</option>
</div>
</div>
</div>
</selectmenu>
Using custom markup to wrap the list of options, the above example creates sections with their own styles and content as seen below:
Examples
You can find multiple examples of <selectmenu>
on our demo page.