Skip to content

Documentation

On this page

Customizable select

.form-select already restyles the closed field. Where the browser implements appearance: base-select, the open list, caret, and checkmark use the same tokens — no dropdown script and no third-party select.

Sizes are minified and gzip. A plugin figure includes the Bootstrap runtime that plugin imports. Shipping every plugin is bootstrap.bundle.min.js at 23.3 KB gzip, not the sum of these rows.
What runs it JavaScript
Bootstrap 5.3 No equivalent. A dropdown or a third-party select plugin. Not in bootstrap.min.js
This repo `appearance: base-select` on `.form-select` 0 KB
Markup this component accepts. The full inventory is /components.json.
Avoid
  • Do not rebuild a select with .dropdown-menu. That is a menu, not a form control.
  • <select multiple> and size other than 1 stay on the OS listbox. This CSS does not opt those in.
  • Do not put links or buttons inside <option>. The select’s button is inert.

How it works

  • Both the <select> and ::picker(select) set appearance: base-select. The closed field keeps Bootstrap’s padding, radius, and colors. The open list reuses .dropdown-menu tokens.
  • ::picker-icon draws the caret from --bs-form-select-bg-img, including dark mode. The SVG background on the closed field is removed so the two do not stack.
  • option:checked uses the dropdown active background and bold text. ::checkmark is extra decoration; it is not exposed to assistive technology.
  • Optional <button><selectedcontent> mirrors the selected option’s markup in the closed field. Browsers that ignore those tags still show the option text.
  • <select multiple> and size other than 1 are left on the OS listbox.

Basic

A labelled .form-select. Open it to see the token-styled picker.

HTML
<label class="form-label" for="plan">Plan</label>
<select class="form-select" id="plan" name="plan">
  <option value="free">Free</option>
  <option value="pro" selected>Pro</option>
  <option value="team">Team</option>
</select>

Sizing

.form-select-sm and .form-select-lg still change the closed field. The picker inherits the control’s font size.

HTML
<div class="mb-3">
  <label class="form-label" for="city-sm">Small</label>
  <select class="form-select form-select-sm" id="city-sm" name="city-sm">
    <option>Copenhagen</option>
    <option selected>Aarhus</option>
    <option>Odense</option>
  </select>
</div>
<div>
  <label class="form-label" for="city-lg">Large</label>
  <select class="form-select form-select-lg" id="city-lg" name="city-lg">
    <option>Copenhagen</option>
    <option selected>Aarhus</option>
    <option>Odense</option>
  </select>
</div>

Rich options

Icons and a second line in the list. selectedcontent hides .form-select-desc in the closed field. Each option keeps readable text for browsers that strip the markup.

HTML
<label class="form-label" for="shipping">Shipping</label>
<select class="form-select" id="shipping" name="shipping">
  <button>
    <selectedcontent></selectedcontent>
  </button>
  <option value="standard" aria-label="Standard, 5 to 7 days">
    <span aria-hidden="true">📦</span>
    <span>
      <span>Standard</span>
      <span class="form-select-desc">5–7 days</span>
    </span>
  </option>
  <option value="express" selected aria-label="Express, 1 to 2 days">
    <span aria-hidden="true">⚡</span>
    <span>
      <span>Express</span>
      <span class="form-select-desc">1–2 days</span>
    </span>
  </option>
  <option value="overnight" aria-label="Overnight, next morning">
    <span aria-hidden="true">🌙</span>
    <span>
      <span>Overnight</span>
      <span class="form-select-desc">Next morning</span>
    </span>
  </option>
</select>

Option groups

optgroup with a legend, styled like a dropdown header. The label attribute still works when legend is missing.

HTML
<label class="form-label" for="animal">Animal</label>
<select class="form-select" id="animal" name="animal">
  <optgroup>
    <legend>Domestic</legend>
    <option value="cat" selected>Cat</option>
    <option value="dog">Dog</option>
  </optgroup>
  <optgroup>
    <legend>Farm</legend>
    <option value="chicken">Chicken</option>
    <option value="cow">Cow</option>
  </optgroup>
</select>

Accessibility

Label with <label for> or wrap the control. Keep a name so the value submits.

Decorative icons inside options take aria-hidden="true". If the mirrored rich content would read badly, set aria-label on the option.

::picker-icon and ::checkmark are not in the accessibility tree. Do not put the only meaning of the control in generated content.

The checked option uses background and font-weight, not color alone. Arrow keys still move in one dimension even if the picker is laid out as a grid.

Browser support

Three columns, not one baseline. The same row is on the overview.
No script Polyfill No fallback
appearance: base-select: Chrome 135+, Safari 27+. Firefox 149 behind dom.select.customizable_select.enabled and layout.css.appearance-base.enabled. None Marked experimental on the forms page. Without base-select the closed .form-select and the OS picker stay. Rich markup inside <option> is stripped to text. <button> / <selectedcontent> inside the select are ignored.