Educational guide to customizable select
Customize a select while keeping its native behavior
Learn what base-select enables, how the button and picker connect, and what a form keeps when decoration is unavailable.
Mental model
A select chooses a value from a closed list. Customizing its presentation does not require rebuilding that interaction in JavaScript.
<button>Surface that opens the dropdown.<selectedcontent>Copy of the selected option content inside the button.::picker-iconSelect opening indicator.::picker(select)Surface containing the dropdown list.<option>Each available choice, with its own value.::checkmarkMark for the selected option inside the list.
appearance: none removes some system styling, but does not enable the customizable picker on its own. base-select activates a different presentation for the control and dropdown; apply it to both. The picker becomes a web surface in the top layer, rather than the mobile system interface.
Think of two surfaces: the button shows the current choice and the picker contains the options. ::picker-icon signals opening; ::checkmark marks the selected option. You can style each part without replacing the select with a collection of divs.
Keep a conventional select when text options and the system experience already solve the task. Customization helps when you need to control the surface and present additional content; it does not turn the field into a search box or allow free-form values.
Semantic HTML
Start with a persistent label and understandable options. This example chooses a delivery method and sends no requests.
Delivery method
The label is associated through for/id. name="delivery" identifies the field and each option’s value determines the form data: choosing Express produces delivery=express, rather than the text with the delivery time. Translating the label should not change that contract.
The first child button contains selectedcontent, which the browser fills with a copy of the chosen option’s content. You do not need to synchronize it manually. If you enrich options, keep useful text, avoid IDs that could be duplicated when cloned, and do not place links or interactive controls inside them.
The CSS shows explicit color, radius, and shape values so you can reuse it without BaselineLab styles. light-dark() preserves both themes with color-scheme. The preview uses those same values through site tokens; playground controls do not change the example.
Styles and states
Each part’s selector determines where a rule applies. Focus, opening, and selection are different states.
select::picker(select)Targets the dropdown, rather than the button. Enable appearance: base-select here too. The browser associates it with the select through an implicit anchor and places it in the top layer; you do not need to add popover to the HTML or fix stacking with z-index.
select::picker-iconStyles the button’s indicator. Keeping an opening cue helps identify the field; the example changes its color and preserves its native shape.
option:checked / option::checkmarkThe first rule describes the selected option; the second, its mark. The example combines text weight with the checkmark so the choice does not depend on color alone. Hover or focus does not mean the value has already changed.
select:open / select:focus-visibleUse :open for presentation while the picker is open and :focus-visible for a noticeable keyboard outline. Do not remove focus when decorating the border. Check options as you navigate them with arrow keys too.
The recipe needs no animation. If you add entry and exit transitions, verify @starting-style, discrete transitions, and overlay separately, and respect prefers-reduced-motion. Picker support does not certify support for that animated recipe.
Support and compatibility
The enhancement should preserve a useful choice even when the browser does not render all the modern parts.
Check the base-select value of appearance and the tables for selectedcontent, ::picker(select), ::picker-icon, and ::checkmark separately. The overall status of appearance as a property does not make all its values interoperable.
@supports (appearance: base-select) limits enhancement styles to browsers that accept that value. It is a syntax query, not a test of opening, cloning, or accessibility. Check the complete behavior in your target browsers. The base preserves the conventional appearance and readable text in every option.
Explicit conventional reference
Common mistakes
Diagnose the part and state before adding more rules or replacing the control.
The picker ignores styles
- Expected result
- The button and list share the customized presentation.
- Cause
- base-select was only enabled on the select, or the browser does not implement the part being used.
- Solution
- Enable it on ::picker(select) too; check its support and test actual opening and selection. Do not infer complete support from CSS.supports().
The selected content loses information
- Expected result
- The choice is understandable in both the button and list.
- Cause
- Information depends on decoration or rich content that is not preserved in the conventional presentation.
- Solution
- Include a readable name in every option. Use a stable value and selectedcontent for presentation; do not use decorative text as form data.
A long option overflows
- Expected result
- Text remains readable on a narrow screen.
- Cause
- base-select no longer sizes the control according to the longest option. An intrinsic width or nowrap can force overflow.
- Solution
- Give the field an adaptive width, allow text wrapping, and constrain the picker to the viewport. Test all choices and enlarged text, rather than only the first.
The control looks good but loses focus
- Expected result
- It can be opened, navigated, selected, and closed with a keyboard.
- Cause
- outline was removed or added logic interferes with native behavior.
- Solution
- Keep visible focus. Test arrows, selection, Escape, and returning to the field; review names and announcements with assistive technologies in your target environments.