The problem with styling selects
The <select> element has been one of the biggest pain points in web development for over two decades. Unlike almost every other HTML element, the browser-rendered dropdown was essentially a black box. You could change the font and colors of the button, but the dropdown list, the options, the arrow indicator — all untouchable.
This led to an entire ecosystem of JavaScript replacements: Select2, Choices.js, React Select, Headless UI Listbox, and dozens more. These libraries replace the native <select> with a fully custom DOM structure, then re-implement keyboard navigation, ARIA attributes, focus management, and form participation from scratch.
The cost? Extra JavaScript weight (Select2 is ~30 KB gzipped), accessibility bugs, maintenance burden, and a fundamentally different element that just looks like a select. With appearance: base-select, that era is over.
Step 1: Enable base-select mode
The opt-in is simple. Apply appearance: base-select to both the <select> and its ::picker(select) pseudo-element:
select,
select ::picker(select) {
appearance: base-select;
} This switches the select to a minimal, fully stylable foundation. The browser strips away its default chrome and gives you raw building blocks: the button, the dropdown, the options, and a new <selectedcontent> element.
Step 2: Style the select button
With base-select enabled, the <select> element itself becomes the button. Style it like any other element:
select {
padding: 12px 16px;
border: 1px solid #ccc;
border-radius: 8px;
font-size: 1rem;
background: #fff;
cursor: pointer;
} You can also style the dropdown arrow indicator using the ::picker-icon pseudo-element, or replace it entirely with a custom icon using ::after.
Step 3: Customize the dropdown
The dropdown list is accessed via ::picker(select). It renders in the top layer, meaning it won't be clipped by parent containers with overflow: hidden. The browser also handles positioning and flipping automatically based on viewport space.
select ::picker(select) {
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 8px;
box-shadow: 0 8px 32px rgba(0,0,0,.12);
} Step 4: Style individual options
Options are fully stylable too. You can even include rich HTML content like images and icons inside them:
option {
padding: 10px 14px;
border-radius: 8px;
display: flex;
align-items: center;
gap: 10px;
}
option:checked {
background: rgba(124, 58, 237, 0.1);
} This means you can build country pickers with flag icons, user selectors with avatars, or color pickers with swatches — all with the native <select> element.
Step 5: Reflect selected content
The new <selectedcontent> element is placed inside the <select> button and mirrors the HTML of the currently selected option. This means if your option has an icon and a label, the button shows the same icon and label.
<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option value="us">
<img src="us.svg"> United States
</option>
</select> You can selectively hide parts of the reflected content. For instance, show only an icon in the button while the full option shows icon + name + description:
selectedcontent .description {
display: none;
} Browser support
As of early 2026, appearance: base-select is supported in Chrome 134+ and Edge 134+. Firefox and Safari are actively implementing it. For browsers without support, the native select works as a perfectly functional fallback — users just see the default browser styling.
This is a true progressive enhancement: the feature is opt-in, the fallback is the native element, and no JavaScript is needed for either path.