Custom select menus

appearance: base-select on <select>

Limited availability
  • Chrome135
  • Edge135
  • Firefox-
  • Safari-

Features it needs

Opting a select into appearance: base-select makes its picker a normal, styleable part of the page, so options can hold markup: an avatar, a swatch, a second line of muted text. The keyboard behaviour, the accessible role and the mobile picker stay the browser's, which is the part custom select components most often get wrong. This covers presentation. It does not turn a select into a combobox.

When this applies

Styling a select's options with markup, such as a flag or a two-line label.

The native approach

<select>
  <button>
    <selectedcontent></selectedcontent>
  </button>
  <option value="de">
    <span aria-hidden="true">DE</span> Germany
  </option>
  <option value="at">
    <span aria-hidden="true">AT</span> Austria
  </option>
</select>

<style>
  select,
  select::picker(select) {
    appearance: base-select;
  }

  select::picker(select) {
    border-radius: 0.5rem;
    padding: 0.25rem;
  }

  option:checked {
    font-weight: 600;
  }
</style>

MDN reference

When the dependency is still right

An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.

  • You need typeahead filtering, multi-select with tags, or options loaded asynchronously from a search. Those make it a combobox, and a select is the wrong element regardless of styling.
  • You need creatable options, where the user can add a value that is not in the list.
  • You need anything beyond Chromium today. Customizable select is limited availability. It degrades to a normal select, which is a safe fallback, but the design will not match.
  • You need grouped options with sticky group headers while scrolling.

Building it

This catalog stops at the swap. These guides go through the implementation and the fallbacks. They come from Google Chrome's modern-web-guidance, Apache-2.0.

Packages this covers