Actions & Overlays
Dropdown Menu
A button-triggered menu following the WAI-ARIA menu button pattern.
A button-triggered menu following the WAI-ARIA menu button pattern, built on the native Popover API. Put popover on the menu and popovertarget on the trigger and the browser handles opening, Escape, light dismiss, and the top layer with no JavaScript. The enhancer adds role="menu" wiring, roving focus, arrow/Home/End/typeahead navigation that skips disabled items, menuitemcheckbox/menuitemradio state, and focus return to the trigger.
Dropdown
Open with a click or Down, then navigate with the arrow keys and typeahead. Esc closes and returns focus to the trigger; clicking outside closes without moving focus.
Checkable items and groups
role="menuitemcheckbox" toggles aria-checked; role="menuitemradio" checks itself and unchecks its siblings in the same role="group". hl:select and onSelect report the new checked state. Pass closeOnSelect: false to keep the menu open while several boxes are toggled.
JavaScript
This component ships an optional enhancer. With auto-init it loads automatically; to wire it yourself, import it directly:
import { enhanceDropdown } from '@hydrateless/enhancers/dropdown';
const handle = enhanceDropdown(document);
// handle.api -> imperative controls
// handle.destroy() -> tear everything downProps
The framework bindings expose these props (and forward the rest to the root element).
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | Not set | Controlled open state; pair with `onOpenChange` (Vue: `v-model:open`, Svelte: `bind:open`). |
defaultOpen | boolean | false | Open on mount. |
onOpenChange | (open: boolean) => void | Not set | Called after the menu opens or closes. |
onSelect | (value: string, item: HTMLElement, checked?: boolean) => void | Not set | Called with the activated item value. For checkable items, `checked` is the new state. |
closeOnSelect | boolean | true | Close the menu after an item is activated. |
placement | Placement | 'bottom-start' | Placement used by the JS positioning fallback when CSS anchor positioning is missing. |
DropdownItem.value | string | Not set | Value reported by `onSelect`; defaults to the item text. |
DropdownItem.role | 'menuitem' | 'menuitemcheckbox' | 'menuitemradio' | 'menuitem' | Plain action, toggle, or single-select item. |
DropdownItem.checked | boolean | false | Initial `aria-checked` for checkable items. |
DropdownItem.disabled | boolean | false | Skipped by arrow navigation and typeahead; cannot be activated. |
DropdownGroup.label | string | Not set | Accessible name of the `role="group"`; scopes `menuitemradio` exclusivity. |
Events and callbacks
| Event / Callback | Payload | Description |
|---|---|---|
hl:open-change | { open: boolean } | Fires after the menu opens or closes (also the `onOpenChange` callback). |
hl:select | { value: string, item: HTMLElement, checked?: boolean } | Cancelable CustomEvent carrying the item's `data-hl-value` (or text). `preventDefault()` vetoes the activation and restores the previous checked state. |
CSS variables
Override these on any ancestor to theme the component. See the theme studio to preview changes live.
| CSS variable | Used for |
|---|---|
--hl-surface | Menu background. |
--hl-border | Menu border. |
--hl-overlay-shadow | Menu elevation. |
Accessibility
- The trigger exposes
aria-haspopup="menu",aria-expanded, andaria-controls; the list isrole="menu"labelled by the trigger. - Arrow keys, Home/End, typeahead, Enter/Space, and
Escare handled; disabled items are skipped and inert. - Focus returns to the trigger when the menu closes via
Esc,Tab, or item activation, but not on light dismiss. - Every
menuitemcheckbox/menuitemradiocarries an explicitaria-checked.