HTML Customizable Select Elements

Modern HTML supports customizable select elements, allowing developers to style more parts of a drop-down control without replacing the native select with a custom JavaScript widget.

The feature builds on the familiar <select> and <option> elements. Supporting browsers can also use a first-child <button>, the <selectedcontent> element, richer option content, and CSS features such as appearance: base-select and ::picker(select).

Basic Customizable Select Structure

A customizable <select> can include a <button> as its first child. The <selectedcontent> element inside that button mirrors the contents of the currently selected <option>.

Example:

<label for="city">Choose a city:</label>

<select id="city">
  <button>
    <selectedcontent></selectedcontent>
  </button>

  <option value="indore">Indore</option>
  <option value="melbourne">Melbourne</option>
  <option value="london">London</option>
</select>

The browser keeps <selectedcontent> synchronized with the selected <option>. You do not need JavaScript just to update the displayed selected value.

Customizable selects are progressive enhancement. Unsupported browsers can still fall back to normal <select> behavior, although advanced styling features may not apply.

Opt In with appearance: base-select

To enable the customizable rendering model, set appearance: base-select on both the <select> and its picker.

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

The <select> represents the closed control. The ::picker(select) pseudo-element represents the pop-up picker that contains the options.

Style the Select and Picker Separately

Once the control uses base-select, the closed button area and the drop-down picker can be styled independently.

Example:

select {
  min-width: 240px;
  padding: 10px;
  border: 1px solid #aaa;
  border-radius: 8px;
}

::picker(select) {
  border: 1px solid #aaa;
  border-radius: 8px;
}

option {
  padding: 10px;
}

option:checked {
  font-weight: bold;
}

This lets you keep native <select> semantics while gaining more control over layout, spacing, borders, backgrounds, and option states.

How selectedcontent Works

The <selectedcontent> element is placed inside the first child <button> of a single <select> control. When the selected <option> changes, the browser clones the option's contents into <selectedcontent>.

This is useful when options contain richer markup such as icons or separate label spans. You can show that richer content inside the picker while presenting a simpler version in the closed control.

<select>
  <button>
    <selectedcontent></selectedcontent>
  </button>

  <option value="design">
    <span class="icon" aria-hidden="true">[D]</span>
    Design
  </option>

  <option value="code">
    <span class="icon" aria-hidden="true">[C]</span>
    Development
  </option>
</select>

Customize the Selected Value

Because <selectedcontent> contains cloned option content, you can target elements inside it without changing how those same elements appear in the picker.

Example:

selectedcontent .icon {
  display: none;
}

option .icon {
  margin-right: 0.5rem;
}

This pattern can keep the closed control compact while still showing richer option content in the drop-down list.

Useful CSS Hooks

Feature Purpose
appearance: base-select Opts the select into customizable rendering
::picker(select) Targets the drop-down picker
::picker-icon Targets the built-in picker arrow
:open Matches the select while its picker is open
:checked Matches the selected option
::checkmark Targets the selected option's checkmark

Form Behavior Remains Native

The <select> still participates in forms like an ordinary select control. Its name and selected <option> determine the submitted value, validation still works, and scripts can continue reading the value property.

const city = document.querySelector("#city");

// Read the same value you would read from a classic select.
console.log(city.value);

This is an important advantage over fully custom widgets, which often require extra JavaScript to reproduce keyboard behavior, form submission, and accessibility semantics.

Compatibility and Progressive Enhancement

Customizable <select> features are newer than traditional select styling and are not supported equally in every browser version. Your form should therefore remain usable when advanced features are ignored.

You can use @supports to apply enhanced styles only where they are recognized.

@supports (appearance: base-select) {
  select,
  ::picker(select) {
    appearance: base-select;
  }
}

Common Mistakes

  • Removing native semantics unnecessarily: keep the <select>, <option>, and <label> structure intact, including the name and value attributes.
  • Assuming every browser supports the full feature set: test fallback behavior as well as the enhanced design.
  • Putting interactive controls inside option content: rich visual content does not mean every nested element should become separately interactive.
  • Forgetting a label: customizable styling does not replace accessible form labeling.

Recommended Practices

  • Use a standard select as the foundation so the control degrades gracefully.
  • Keep option values explicit when visible option content contains multiple elements.
  • Use selectedcontent to simplify how the chosen value appears in the closed control.
  • Test keyboard navigation, focus indication, zoom, and high-contrast modes after applying custom styles.

Conclusion

Customizable selects bring much more styling control to native HTML form controls. By combining <select>, <option>, a first-child <button>, <selectedcontent>, and base-select styling, you can build richer drop-down interfaces while preserving normal form behavior. Use the feature progressively so unsupported browsers still receive a functional <select>.



Found This Page Useful? Share It!
Get the Latest Tutorials and Updates
Join us on Telegram