<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>
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 thearia-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
anddata-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 thearia-controls
attribute of the trigger.aria-labelledby="{ BUTTON_ID }"
: linked to by the button'sid
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 thearia-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 ondocument
. 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 inevent.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
<div id="select-410742" class="select">
<button type="button" class="btn-outline justify-between font-normal w-[200px]" id="select-410742-trigger" aria-haspopup="listbox" aria-expanded="false" aria-controls="select-410742-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-410742-popover" data-popover aria-hidden="true" class="w-72">
<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 timezone..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="false" aria-controls="select-410742-listbox" aria-labelledby="select-410742-trigger" />
</header>
<div role="listbox" id="select-410742-listbox" aria-orientation="vertical" aria-labelledby="select-410742-trigger" data-empty="No timezone found.">
<div class="max-h-64 overflow-y-auto scrollbar">
<div role="group" aria-labelledby="demo-combobox-timezones-group-0">
<div role="heading" id="demo-combobox-timezones-group-0">Americas</div>
<div role="option" data-value="America/New_York">(GMT-5) New York</div>
<div role="option" data-value="America/Los_Angeles">(GMT-8) Los Angeles</div>
<div role="option" data-value="America/Chicago">(GMT-6) Chicago</div>
<div role="option" data-value="America/Toronto">(GMT-5) Toronto</div>
<div role="option" data-value="America/Vancouver">(GMT-8) Vancouver</div>
<div role="option" data-value="America/Sao_Paulo">(GMT-3) São Paulo</div>
</div>
<div role="group" aria-labelledby="demo-combobox-timezones-group-1">
<div role="heading" id="demo-combobox-timezones-group-1">Europe</div>
<div role="option" data-value="Europe/London">(GMT+0) London</div>
<div role="option" data-value="Europe/Paris">(GMT+1) Paris</div>
<div role="option" data-value="Europe/Berlin">(GMT+1) Berlin</div>
<div role="option" data-value="Europe/Rome">(GMT+1) Rome</div>
<div role="option" data-value="Europe/Madrid">(GMT+1) Madrid</div>
<div role="option" data-value="Europe/Amsterdam">(GMT+1) Amsterdam</div>
</div>
<div role="group" aria-labelledby="demo-combobox-timezones-group-2">
<div role="heading" id="demo-combobox-timezones-group-2">Asia/Pacific</div>
<div role="option" data-value="Asia/Tokyo">(GMT+9) Tokyo</div>
<div role="option" data-value="Asia/Shanghai">(GMT+8) Shanghai</div>
<div role="option" data-value="Asia/Singapore">(GMT+8) Singapore</div>
<div role="option" data-value="Asia/Dubai">(GMT+4) Dubai</div>
<div role="option" data-value="Australia/Sydney">(GMT+11) Sydney</div>
<div role="option" data-value="Asia/Seoul">(GMT+9) Seoul</div>
</div>
</div>
<hr role="separator" />
<div role="option">
<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">
<circle cx="12" cy="12" r="10" />
<path d="M8 12h8" />
<path d="M12 8v8" />
</svg>
Create timezone
</div>
</div>
</div>
<input type="hidden" name="select-410742-value" value="" />
</div>
Top side
<div id="select-437723" class="select">
<button type="button" class="btn-outline justify-between font-normal w-[200px]" id="select-437723-trigger" aria-haspopup="listbox" aria-expanded="false" aria-controls="select-437723-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-437723-popover" data-popover aria-hidden="true" data-side="top">
<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-437723-listbox" aria-labelledby="select-437723-trigger" />
</header>
<div role="listbox" id="select-437723-listbox" aria-orientation="vertical" aria-labelledby="select-437723-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-437723-value" value="" />
</div>