v1.2.0
  1. ds
  2. Components
  3. Combobox and tags

Combobox and tags

Type to filter, pick many, remove with a click.

Combobox

Code
<div class="combo">
  <div class="inp-group">
    <svg class="i">…</svg>
    <input class="inp mono" role="combobox" aria-expanded="true" aria-controls="cb-list" aria-autocomplete="list" aria-activedescendant="cb-1" />
  </div>
  <div class="menu" role="listbox" id="cb-list">
    <div class="mi" role="option" id="cb-0" aria-selected="true"><span><mark>order</mark>s</span></div>
    <div class="mi" role="option" id="cb-1" data-active><span><mark>order</mark>_items</span></div>
  </div>
</div>

An input that filters a list. It reuses the menu surface and items, opened under the input at the same width. The matching part of each option is in the accent; the chosen one gets a check. Focus stays in the input the whole time: arrow keys move aria-activedescendant, Enter picks, Escape closes.

Tags

team:data env:prod pii
Enter or comma adds a label. Backspace in an empty field removes the last one.
Code
<label class="fld" for="ti"><span>Labels</span></label>
<div class="tags-input" id="tags">
  <span class="chip">team:data<button class="rm" type="button" aria-label="Remove team:data"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button></span>
  <span class="chip">env:prod<button class="rm" type="button" aria-label="Remove env:prod"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button></span>
  <span class="chip warm">pii<button class="rm" type="button" aria-label="Remove pii"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button></span>
  <input id="ti" placeholder="Add a label" autocomplete="off" />
</div>
<span class="hint">Enter or comma adds a label. Backspace in an empty field removes the last one.</span>

Chips and a bare input in one frame that looks and focuses like an .inp. Each chip has a remove button with its own label. Use .warm chips for values that mark data, such as PII.

Filter chips

status: failed owner: priya
Code
<div class="chips">
  <span class="chip on">status: failed<button class="rm" type="button" aria-label="Clear status filter"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button></span>
  <span class="chip on">owner: priya<button class="rm" type="button" aria-label="Clear owner filter"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button></span>
  <button class="chip" type="button"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>Add filter</button>
</div>

Active filters above a table are the same chip with .on. The remove button sits inside the chip, so the chip itself can still open an editor for that filter.

API

ClassOnWhat it does
.combodivWraps the input and positions the .menu under it at full width.
[role=option].miAn option. aria-selected="true" adds the check; data-active marks the keyboard position.
mark.miThe part of the option that matches the query.
.menu-emptypShown in the menu when nothing matches.
.tags-inputdivChips and a bare <input> in one frame.
.rmbutton in .chipThe remove button. Needs an aria-label.
Do
  • A combobox past about ten options, a select below.
  • Say what happens with no match: "No table named ordrs. Create it?"
  • Keep typed text when the menu closes.
Don't
  • Autoselecting the first match on blur.
  • Tags without a remove button.
  • Free text in a field that only accepts known values.

Accessibility

  • The input has role="combobox", aria-expanded and aria-controls; the list has role="listbox". Focus never leaves the input.
  • Announce the result count in a polite live region when it changes: "3 tables".