Skip to content

ExpressiveCSS

Browse documentation

Select

Choose one option, or several, from a styled menu.

View as Markdown

Wrap a native <select> in .field and give the label a matching for. Select turns it into an exposed menu, a text field paired with a menu. Add outlined (or border) on that field for the outlined variant. AutoInit() starts every select except those marked no-autoinit. Add browser-default to keep the native control.

Filled enhanced selects reserve space for their label, including translated labels that wrap or grow with text size. The outlined and native variants keep their existing layout.

Add multiple to select several options. Chosen values appear as a comma-separated list. Distinct options with the same value can be selected and deselected independently.

Native <optgroup> elements become group headings in the menu.

Put an image URL in data-icon on an option. The component copies that option's classes to the image. Images float right by default, and left floats one left. The <select> needs no icons class. Supported URLs are relative, HTTP(S), blob, and image data URLs (AVIF, BMP, GIF, JPEG, PNG, SVG, WebP, or ICO). The component ignores invalid URLs and other schemes, and the option stays selectable.

Add browser-default to skip the menu and keep the native select.

<div class="field">
  <select id="form-select-1">
    <option value="" disabled selected>Choose your option</option>
    <option value="1">Option 1</option>
    <option value="2">Option 2</option>
    <option value="3">Option 3</option>
  </select>
  <label for="form-select-1">Expressive Select</label>
</div>

<div class="field">
  <select id="form-select-2" multiple>
    <option value="" disabled selected>Choose your option</option>
    <option value="1">Option 1</option>
    <option value="2">Option 2</option>
    <option value="3">Option 3</option>
  </select>
  <label for="form-select-2">Multiple Select</label>
</div>

<div class="field">
  <select id="form-select-3">
    <optgroup label="team 1">
      <option value="1">Option 1</option>
      <option value="2">Option 2</option>
    </optgroup>
    <optgroup label="team 2">
      <option value="3">Option 3</option>
      <option value="4">Option 4</option>
    </optgroup>
  </select>
  <label for="form-select-3">Optgroups</label>
</div>

<div class="field">
  <select id="form-select-4">
    <option value="" disabled selected>Choose your option</option>
    <option value="1" data-icon="photo.jpg">example 1</option>
    <option value="2" data-icon="photo.jpg" class="left">example 2</option>
  </select>
  <label for="form-select-4">Images in select</label>
</div>

<label for="form-select-6">Browser Select</label>
<select id="form-select-6" class="browser-default">
  <option value="" disabled selected>Choose your option</option>
  <option value="1">Option 1</option>
</select>

Initialization

The IIFE bundle exposes Expressive.FormSelect. Call init yourself when you need options other than the defaults, after adding a select dynamically, or after changing an existing select's options. Otherwise Expressive.AutoInit() starts every select.

document.addEventListener('DOMContentLoaded', function() {
  const elems = document.querySelectorAll('select');
  const instances = Expressive.FormSelect.init(elems, {
    // specify options here
  });
});

You can also pass per-instance options through AutoInit:

Expressive.AutoInit(document.body, {
  FormSelect: { menuOptions: { constrainWidth: false } }
});

Options

Name Type Default Description
searchableBooleanfalseAdd a popup search field to an enhanced plain single or multiple select.
i18nObjectsearchLabel: "Search options", noResults: "No results"Override either search label or empty-result message. Omitted strings retain their defaults.
classes String '' Space-separated classes added to the .field wrapper, either the parent .field or one the plugin creates.
menuOptions Object {} Options passed to Menu. See Menu. The component forces coverTrigger and closeOnClick to false.

There is no selected option. Mark options with the HTML selected attribute instead.

Methods

All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.FormSelect.getInstance(elem);
.getSelectedValues();

Returns the selected values as an array of strings.

instance.getSelectedValues();
.refresh();

Re-read the native <select>. Call this after changing select.value, option labels, options or disabled states. It rebuilds the menu rows and leaves the field and any existing Menu instance in place. Refreshing an open menu keeps focus on a surviving option. These programmatic updates do not emit a user change event.

instance.refresh();
.destroy();

Destroy the plugin instance, remove the menu, and restore the native select. Associated labels keep their original nodes and event listeners. Teardown restores their attributes and authored positions.

instance.destroy();

Native forms

The native select handles selection and form submission. The generated menu does not let users choose disabled options or options in disabled optgroups. A selected disabled option stays visible in the field, but native form submission excludes it. An empty selection displays an empty field.

The enhanced field exposes one combobox to assistive technology. All associated labels contribute to its name in their original order, unless an authored aria-label or aria-labelledby supplies the name. Each label targets the visible control. While initialized, the native select stays visually hidden and out of the accessibility tree.

Required and custom validation errors use the native select's browser-provided message. The component shows it as supporting text associated with the visible field. Native validation chooses which field receives focus, and the enhanced select forwards that focus to its visible field. checkValidity() preserves focus. reportValidity() on a select reports that control even when an earlier field is invalid.

After changing setCustomValidity() from script, call refresh() to update an existing error. Correcting the value clears resolved errors. Resetting the form clears displayed errors without changing native validation rules. destroy() removes generated error messages and restores the native control.

Use the form's Reset button or form.reset() to restore defaults. The generated field synchronizes after the browser resets the native control, without a change event. Canceling the reset preserves selection.

Properties

Name Type Description
el Element The native <select> the plugin was initialized with.
options Object The options the instance was initialized with.
isMultiple Boolean Whether this is a multiple select.
wrapper Element The .field wrapper, either the parent .field or one the plugin creates.
menuEl Element The generated popup. A <menu> by default, or a dialog <div> containing a listbox when searchable.
labelEl Element The associated label, or null if none was found.
input Element The text input that shows the current selection.
menu Menu The Menu instance for this select.

Disabled styles

disabled on the <select> disables the whole control. disabled on an <option> makes that item unselectable.

<div class="field">
  <select id="form-select-7" disabled>
    <option value="" disabled selected>Choose your option</option>
    <option value="1">Option 1</option>
  </select>
  <label for="form-select-7">Disabled Select</label>
</div>

<label for="form-select-8">Browser Disabled</label>
<select id="form-select-8" class="browser-default" disabled>
  <option value="" disabled selected>Choose your option</option>
  <option value="1">Option 1</option>
</select>

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow