Combobox

Autocomplete input and command palette with a list of suggestions.

Usage

HTML + JavaScript

Step 1: Include the JavaScript files

You can either include the JavaScript file for all the components, or just the one for this component by adding this to the <head> of your page:

<script src="https://cdn.jsdelivr.net/npm/basecoat-css@0.2.5/dist/js/select.min.js" defer></script>

Step 2: Add your combobox HTML

Combobox uses the same markup and JavaScript as the Select component, the only difference being the search box at the top of the listbox:

<div id="select-575809" class="select">
  <button type="button" class="btn-outline justify-between font-normal w-[200px]" id="select-575809-trigger" aria-haspopup="listbox" aria-expanded="false" aria-controls="select-575809-listbox">
    <span class="truncate"></span>

    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevrons-up-down-icon lucide-chevrons-up-down text-muted-foreground opacity-50 shrink-0">
      <path d="m7 15 5 5 5-5" />
      <path d="m7 9 5-5 5 5" />
    </svg>
  </button>
  <div id="select-575809-popover" data-popover aria-hidden="true">
    <header>
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search">
        <circle cx="11" cy="11" r="8" />
        <path d="m21 21-4.3-4.3" />
      </svg>
      <input type="text" value="" placeholder="Search framework..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="false" aria-controls="select-575809-listbox" aria-labelledby="select-575809-trigger" />
    </header>

    <div role="listbox" id="select-575809-listbox" aria-orientation="vertical" aria-labelledby="select-575809-trigger" data-empty="No framework found.">
      <div role="option" data-value="Next.js">Next.js</div>

      <div role="option" data-value="SvelteKit">SvelteKit</div>

      <div role="option" data-value="Nuxt.js">Nuxt.js</div>

      <div role="option" data-value="Remix">Remix</div>

      <div role="option" data-value="Astro">Astro</div>
    </div>
  </div>
  <input type="hidden" name="select-575809-value" value="" />
</div>

HTML structure

<div class="select">
Wraps around the entire component.
<button type="button" popovertarget="{ POPOVER_ID }">

The trigger to open the popover, can also have the following attributes:

  • id="{BUTTON_ID}": linked to by the aria-labelledby attribute of the listbox.
  • aria-haspopup="listbox": indicates that the button opens a listbox.
  • aria-controls="{ LISTBOX_ID }": points to the listbox's id.
  • aria-expanded="false": tracks the popover's state.
  • aria-activedescendant="{ OPTION_ID }": points to the active option's id.
<div popover class="popover" id="{ POPOVER_ID }">
As with the Popover component, you can set up the side and alignment of the popover using the data-side and data-align attributes.
<header>
Contains the search input to filter the options in the listbox.
<div role="listbox">
The listbox containing the options. Should have the following attributes:
  • id="{ LISTBOX_ID }": refered to by the aria-controls attribute of the trigger.
  • aria-labelledby="{ BUTTON_ID }": linked to by the button's id attribute.
<div role="option" data-value="{ VALUE }">
Option that can be selected.Should have a unique id if you use the aria-activedescendant attribute on the trigger.
<hr role="separator"> Optional
Separator between groups/options.
<div role="group"> Optional
Group of options, can have a aria-labelledby attribute to link to a heading.
<span role="heading">
Group heading, must have an id attribute if you use the aria-labelledby attribute on the group.
<input type="hidden" name="{ NAME }" value="{ VALUE }"> Optional
The hidden input that holds the value of the field (if needed).

JavaScript events

basecoat:initialized
Once the component is fully initialized, it dispatches a custom (non-bubbling) basecoat:initialized event on itself.
basecoat:popover
When the popover opens, the component dispatches a custom (non-bubbling) basecoat:popover event on document. Other popover components (Combobox, Dropdown Menu, Popover and Select) listen for this to close any open popovers.
change
When the selected value changes, the component dispatches a custom (bubbling) change event on itself, with the selected value in event.detail (e.g. {detail: {value: "something"}}).

JavaScript methods

selectByValue

You can call this method on the component after it is initialized to select an option by value (i.e. the option with the matching data-value attribute):

<script>
  const comboboxComponent = document.querySelector("#my-combobox");
  comboboxComponent.addEventListener("basecoat:initialized", () => {
    comboboxComponent.selectByValue("apple");
  });
</script>

Jinja and Nunjucks

You can use the select() Nunjucks or Jinja macro for this component. If you use one of the macros, make sure to set is_combobox to True (or true for Nunjucks).

{% call select(
  listbox_attrs={"data-empty": "No framework found."},
  is_combobox=true
) %}
  <div role="option" data-value="nextjs">Next.js</div>
  <div role="option" data-value="sveltekit">SvelteKit</div>
  <div role="option" data-value="nuxtjs">Nuxt.js</div>
  <div role="option" data-value="remix">Remix</div>
  <div role="option" data-value="astro">Astro</div>
{% endcall %}

Examples

Scrollable

Top side