Combobox

Autocomplete input and command palette with a list of suggestions.

Next.js
SvelteKit
Nuxt.js
Remix
Astro

Usage

HTML + JavaScript

Step 1: Include the JavaScript files

Include the select.js file. It is recommended to also include the CSS anchor positioning polyfill to support older browsers. Add this to the <head> of your page:

<script src="https://cdn.jsdelivr.net/npm/basecoat-css@beta/dist/js/select.min.js" defer></script>
<script type="module">
  if (!("anchorName" in document.documentElement.style)) {
    import("https://cdn.jsdelivr.net/npm/basecoat-css@beta/dist/js/css-anchor-positioning.min.js");
  }
</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 class="select">
  <button type="button" class="btn-outline justify-between font-normal w-[200px]" id="select-527827-trigger" popovertarget="select-527827" aria-haspopup="listbox" aria-expanded="false" aria-controls="select-527827-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 popover id="select-527827" class="popover">
    <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="true" aria-controls="select-527827-content" aria-labelledby="select-527827-trigger" />
    </header>

    <div role="listbox" id="select-527827-listbox" aria-orientation="vertical" aria-labelledby="select-527827-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-527827-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).

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

Americas
(GMT-5) New York
(GMT-8) Los Angeles
(GMT-6) Chicago
(GMT-5) Toronto
(GMT-8) Vancouver
(GMT-3) São Paulo
Europe
(GMT+0) London
(GMT+1) Paris
(GMT+1) Berlin
(GMT+1) Rome
(GMT+1) Madrid
(GMT+1) Amsterdam
Asia/Pacific
(GMT+9) Tokyo
(GMT+8) Shanghai
(GMT+8) Singapore
(GMT+4) Dubai
(GMT+11) Sydney
(GMT+9) Seoul

Create timezone

Top side

Next.js
SvelteKit
Nuxt.js
Remix
Astro