mica
mica

Example

Basic usage

<m-combobox>
  <input list="langs" placeholder="Language&#8230;" />
  <datalist id="langs">
    <option>Ard</option>
    <option>Go</option>
  </datalist>
</m-combobox>

<script type="module" src="mica/combobox.js"></script>

API

m-combobox enhances an authored native input and datalist. Native markup remains the no-JavaScript autocomplete fallback. Optional attributes extend filtering and result identity.

Content/markup contracts

ContentContractDefaultDescription
inputlist references the datalist IDRemains the value, focus, labeling, and event target. Give it an accessible name.
datalistauthored optionsIs the sole source of options. It may be populated asynchronously; the module observes option and value changes.

Styling

TargetValueDefaultDescription
m-comboboxpositioning wrapperblockThe enhanced listbox positions inside this wrapper rather than in the top layer.

combobox.js

Optional. Replaces the native popup with an ARIA combobox/listbox presentation, local or manual filtering, pointer selection, and Arrow, Enter, and Escape handling. It manages expanded, controls, active-descendant, selected, and autocomplete semantics.

Selection writes through the native input value setter so controlled-input frameworks can observe it, then dispatches bubbling input and change events. Without the module, the native datalist remains functional.

<script type="module" src="mica/combobox.js"></script>

Accessibility

The module wires the ARIA combobox pattern while keeping focus on the input. The listbox works in every browser Mica supports because it remains inside the wrapper rather than using the top layer.

Search-backed results

Try the location-search example: duplicate city names, server-style matching, loading, empty and error feedback. The example uses local sample data; applications own fetching and saving.

<m-combobox filter="manual">
  <input list="locations" aria-label="Location">
  <datalist id="locations">
    <option value="Paris" data-key="paris-fr" label="Île-de-France, France"></option>
    <option value="Paris" data-key="paris-tx" label="Texas, United States"></option>
  </datalist>
  <p role="status" hidden>Searching…</p>
</m-combobox>

Omit filter for the default local substring matching. filter="manual" displays the supplied options without filtering them again. Keep fetching and stale-request cancellation in application code.

value is the primary text and the value written into the input. An optional native label adds secondary detail when different from the value. data-key identifies an exact result; it defaults to the option value. Supply distinct keys when labels repeat. Disabled options are visible but cannot be chosen.

Selection event

combobox.addEventListener('m-on-change', ({ detail }) => {
  const location = resultsByKey.get(detail.key);
  // detail.value is the text written into the input.
});

m-on-change bubbles from the combobox only when an option is chosen, with { key, value } in detail. Typing emits the input's ordinary events. Selection preserves the existing bubbling input and change events, then emits m-on-change. If you fetch on input, ignore selection-generated input events (for example, check event.isTrusted) so selecting a result does not start another request.

Search feedback

Author one direct child with role="status". Your app controls its text and hidden state. The enhancement places it beside the listbox inside the same popup, outside the selectable options. It can accompany results or appear on its own. Use it for guidance, loading, no results, and errors; Mica supplies no messages. Its polite live-region semantics announce updates without moving focus.

The popup opens on focus, typing, or arrow navigation, and closes on selection, Escape, Tab, or focus leaving the component. Late results do not reopen a dismissed popup until the user interacts again. Removing the component restores the authored datalist connection, input attributes, and status position.

← tabscommand palette →