Elements

Comboboxes

Collect entries or chips in filterable fields.


Setup

Combobox styles are included in Manifest CSS or a standalone stylesheet, both referencing theme variables. The plugin is included in manifest.js with all core plugins, or it can be loaded on its own.

<!-- Manifest CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.css" />

<!-- Manifest JS -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>

Triggers

Add x-combobox to the element that holds the selection and opens the list. Three elements work as triggers, and the rest of this guide uses an input.

Input

Type to filter or to enter free text.


<input x-combobox="countries" placeholder="Country">

Textarea

Textareas are effectively the same as inputs in style and function. The .multiple modifier enabled both inputs and textareas to support multiple entries, which wrap and grow the field's height on overflow.


<textarea x-combobox.multiple.chips="skills" placeholder="Add skills"></textarea>

Button

Buttons behave like a select menu trigger, with no typing input. A single field shows the choice as the button's text. A multiple field shows chips.


<button x-combobox="priority">Select priority</button>
<menu popover id="priority">
    <li>Low</li>
    <li>Medium</li>
    <li>High</li>
</menu>

Chips

Add .chips to show selections as removable chips, and .multiple to allow more than one. Type a value and press Enter, or a comma, to commit it as a chip. Press Backspace on an empty field to remove the last one.


<input x-combobox.multiple.chips placeholder="Add emails">

Separators

Set your own commit keys with the separators option, where each character commits the current entry. So ", " commits on a comma or a space. Pasting text that already contains a separator splits it into several chips at once.


<input x-combobox.multiple.chips="{ separators: ', ' }" placeholder="Add tags">

Starting Values

Seed the field with a starting set using the value attribute, written with the same separators.


<input x-combobox.multiple.chips value="ana@acme.com, sam@acme.com">

Suggestions List

Point a field at a list of options to filter as the user types. Name the list by its id in the directive value, the same way dropdowns reference their menu. The list is a <datalist> or <select> kept in the page as the source.

A single field lets the user pick one option or type their own. Re-opening it shows the whole list again, so the choice can be swapped.


<input x-combobox="countries" placeholder="Country">
<datalist id="countries">
    <option>Australia</option>
    <option>Brazil</option>
    <option>Canada</option>
</datalist>

Add .multiple.chips to choose several. Chosen options drop out of the list as they are picked.


<input x-combobox.multiple.chips="skills" placeholder="Skills">
<datalist id="skills">
    <option>Design</option>
    <option>Writing</option>
    <option>Research</option>
</datalist>

For richer rows, author the list as a <menu popover> instead of a datalist, same as dropdown menus. Give each item a data-value for the value to store and a data-label for the chip text. Everything inside the item is free to hold extra content.


<input x-combobox.multiple.chips="people" placeholder="Assign people">
<menu popover id="people">
    <li data-value="ana" data-label="Ana Ruiz">Ana Ruiz <span class="trailing">ana@acme.com</span></li>
    <li data-value="sam" data-label="Sam Cole">Sam Cole <span class="trailing">sam@acme.com</span></li>
</menu>

Options

Beyond modes, settings are passed as an object in the directive value. The bare id form is shorthand for the source key, so x-combobox="people" and x-combobox="{ source: 'people' }" are the same.

Option Purpose
source ID of the <datalist>, <select>, or <menu> holding the options
max Most selections allowed; omit for uncapped selections
filter How typed text matches options. See below
separators Characters that commit the current entry as a chip
min Characters to type before remote results are fetched
debounce Milliseconds to wait after typing before fetching. Default 200

Filtering

Set how typed text matches options with the filter option.

Value Match
includes Text appears anywhere in the option (default mode)
startswith Option begins with the text
none No filtering; always show every option
pattern Match each option's own data-pattern expression against the input
<input x-combobox="{ source: 'countries', filter: 'startswith' }">

Modifiers

New Values

A field accepts anything the user types by default. Add .create to show an explicit "Add …" row for the current text when it does not match an option.


<input x-combobox.multiple.chips.create="skills" placeholder="Skills">

To restrict the field to the list and reject anything else, add .strict instead.


Limit

Cap the number of selections with the max option. Once the limit is reached the field stops accepting input until a chip is removed.


<input x-combobox.multiple.chips="{ source: 'skills', max: 3 }" placeholder="Pick up to 3">

Remote Suggestions

Add .async to fetch options as the user types instead of filtering a fixed list. On each keystroke the field sends a combobox-filter event carrying the typed value and a setOptions callback. Call setOptions with your results to fill the list.

<input x-combobox.async="{ min: 1 }" placeholder="Search users"
    @combobox-filter="
        fetch('/api/users?q=' + $event.detail.value)
            .then(r => r.json())
            .then(users => $event.detail.setOptions(users))
    ">

Each result is { value, label }, or a plain string when the two are the same. The field opens on focus, shows a brief loading state while it waits, and ignores out-of-date responses. Use min to set how many characters are needed before fetching, and debounce to set the pause after typing.


Form Participation

Add a name attribute to submit with a <form>. A single text field submits its value directly. A field with chips, or any multiple field, submits one entry per value under that name, which arrives as a list on the server.

<form>
    <input x-combobox.multiple.chips="tags" name="tags" value="design, research">
    <button type="submit">Save</button>
</form>

Localization

The few built-in UI strings are overridable through the universal _ui block, namespaced under combobox, in any local data file your project loads. Override only the keys you want. Anything omitted stays in English, and switching locale updates them live.

{
    "data": {
        "content": {
            "en": "/data/content.en.yaml",
            "fr": "/data/content.fr.yaml"
        }
    }
}

The add string places the typed text with a {value} token, so translations keep their own word order and punctuation.

Key Default Used for
empty No matches Shown when nothing matches
add Add “{value}” The .create row label
loading Searching… Shown while remote results load
prompt Type to search Remote field before enough is typed

Keyboard

Key Action
Move through the list
Enter Select the highlighted option, or commit typed text
Backspace Remove the last chip when the field is empty
Separator Commit the current text as a chip
Escape Close the list

Styles

Theme

Comboboxes use the following theme variables.

Variable Purpose
--color-field-surface Field background, and the highlighted option
--color-field-surface-hover Field hover background
--color-field-inverse Field text
--color-popover-surface Suggestion menu and chip background
--color-content-stark Chip text and focus ring
--color-content-neutral Chip remove icon and the "Add …" row
--color-content-subtle Caret and empty-state text
--color-negative-surface / --color-negative-inverse Invalid chip
--radius Field, chip, and menu corners
--spacing-field-height Field height
--spacing-field-padding Field padding
--icon-chevron-down Button trigger caret
--transition Interactive transitions

Customization

The markup is semantic, so the parts are plain selectors.

Selector Part
.combobox The field shell
.combobox-chip A chip, with its label in a span and remove control in a button
menu[role=listbox] The suggestion menu
[role=option] An option
[role=option][aria-selected=true] A chosen option
[role=option][aria-current=true] The highlighted option
/* Pill-shaped chips */
.combobox-chip {
    border-radius: 100px;
}
  • Australia
  • Brazil
  • Canada
  • Denmark
  • France
  • Germany
  • Japan
  • Mexico
  • Spain
  • Sweden
  • Design
  • Writing
  • Research
  • Strategy
  • Testing
  • Australia
  • Brazil
  • Canada
  • Denmark
  • France
  • Germany
  • Japan
  • Mexico
  • Spain
  • Sweden
  • Design
  • Writing
  • Research
  • Strategy
  • Testing
  • Design
  • Writing
  • Research
  • Strategy
  • Design
  • Writing
  • Research
  • Strategy
  • Testing