Listbox
shadcn-htmx/listboxFreeComponent
A scrollable single- or multi-select list built on role="listbox" with role="option" children, following the WAI-ARIA APG keyboard contract. A hidden input mirrors the selection so it submits like a normal form field.
Live preview
live · rendered by the registry
Rendered by shadcn-htmx on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://shadcn-htmx.productdevbook.com/r/listbox.jsonSource
1/** @jsxImportSource hono/jsx */2import type { PropsWithChildren } from "hono/jsx"3import { cn, type ClassValue } from "@/registry/lib/cn"45// Listbox — shadcn-htmx, htmx v4 + Tailwind v4.6//7// shadcn/ui builds its rich Select on a Radix popover with role="listbox"8// options. We split that into two components: `Select` is the truly-native9// dropdown (<select>), and this `Listbox` is the always-visible, scrollable,10// single/multi-select widget — the APG Listbox pattern. We mirror shadcn's11// anatomy (a container + option children) but translate it to a real12// role="listbox" / role="option" tree, not a Radix portal.13// Anatomy reference (intent only): repos/shadcn-ui/apps/v4/registry/new-york-v4/ui/select.tsx14//15// Accessibility contract follows the WAI-ARIA APG Listbox pattern:16// repos/aria-practices/content/patterns/listbox/listbox-pattern.html17// (roles/states: listbox, option, aria-selected, aria-multiselectable,18// aria-orientation; keyboard: Up/Down/Home/End, Space/Enter, type-ahead,19// Shift/Ctrl range + Ctrl+A for multi-select)20// repos/aria-practices/content/patterns/listbox/examples/listbox-scrollable.html21// (the scrollable single-select example whose markup we mirror: a <ul>22// with role="listbox" holding <li role="option"> children)23//24// Focus management — roving tabindex (NOT aria-activedescendant). The APG25// permits either; the rest of this library (Toolbar, Tabs, Menu) uses a26// roving tabindex, so we keep DOM focus on the options for consistency. The27// container <ul> is tabindex="-1"; exactly one <li role="option"> carries28// tabindex="0" (the first selected option, else the first option) and the29// rest tabindex="-1". An inline boot <script> sets that before paint (no30// flicker); public/site.js (keyed on data-slot="listbox") owns the live31// keyboard + selection contract and keeps the hidden form input in sync.32// Roving tabindex rationale: repos/aria-practices/content/practices/keyboard-interface/keyboard-interface-practice.html33//34// Selection state uses aria-selected (APG's recommended convention for35// single-select; we keep it for multi-select too so the markup is uniform).36// The container sets aria-multiselectable="true" when multiple.37// repos/mdn/files/en-us/web/accessibility/aria/reference/roles/listbox_role/index.md38// repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-selected/index.md39//40// … truncated
More from shadcn-htmx
All 83 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | shadcn-htmx | Components | Free | no deps | |
| Active Searchactive-search | shadcn-htmx | Components | Free | no deps | |
| Alertalert | shadcn-htmx | Components | Free | no deps | |
| Alert Dialogalert-dialog | shadcn-htmx | Components | Free | no deps | |
| Aspect Ratioaspect-ratio | shadcn-htmx | Components | Free | no deps | |
| Auto Gridauto-grid | shadcn-htmx | Components | Free | no deps | |
| Autocompleteautocomplete | shadcn-htmx | Components | Free | no deps | |
| Autosize Textareaautosize-textarea | shadcn-htmx | Components | Free | no deps |
