Skip to content
Home Theme Gallery

Command

The custom element is <hui-command>.

Overview

A modal command palette: a search input over a filtered, grouped listbox. Selecting a command emits hui-select and the host decides what it means, because authorisation and side effects are server-owned.

Example

Command palette
Source
<hui-command label="Command menu" placeholder="Search commands">
<button slot="trigger">Open command</button>
<div role="option" data-value="new">New file</div>
<div role="option" data-value="settings">Settings</div>
</hui-command>

The Go template that renders it:

<hui-command label="Command menu" placeholder="Search commands">
<button slot="trigger">Open command</button>
{{ range .Commands }}
<div role="option" data-value="{{ .Value }}">{{ .Label }}</div>
{{ end }}
</hui-command>

API

Attributes3
Attributes
NameTypeDefaultDescription
openbooleanfalseWhether the dialog is open. Reflects to the open attribute and drives showModal().
placeholderstring"Type a command or search…"Placeholder shown in the search input.
labelstring"Command menu"Accessible name for the dialog, applied as aria-label.
Properties3
Properties
NameTypeDefaultDescription
openbooleanfalseReflects to the open attribute.
placeholderstring'Type a command or search…'Reflects to the placeholder attribute.
labelstring'Command menu'Reflects to the label attribute.
Methods2
Methods
NameTypeDescription
show-Opens the palette. An optional element argument is remembered as the trigger to restore focus to on close.
hide-Closes the palette.
Events3
Events
NameDescription
hui-selectEmitted when a command is chosen, with detail.value and detail.option. It bubbles and is composed.
hui-openEmitted once the dialog is shown. It bubbles and is composed.
hui-closeEmitted once the dialog is closed. It bubbles and is composed.
Slots2
Slots
NameDescription
triggerThe element that opens the palette when clicked.
(default)The commands, each a role="option" with a data-value, either directly or inside a role="group" wrapper.
CSS custom properties5
CSS custom properties
NameDefaultDescription
--hui-popoveroklch(1 0 0)Background of the dialog.
--hui-radius-xlcalc(var(--hui-radius, 0.45rem) * 1.4)Corner radius of the dialog.
--hui-shadow-lg0 10px 15px -3px oklch(0 0 0 / 0.1), 0 4px 6px -4px oklch(0 0 0 / 0.1)Elevation of the dialog.
--hui-borderoklch(0.922 0.005 325.62)Divider under the search field.
--hui-accentoklch(0.96 0.003 325.6)Background of the active command.
::part() hooks6
::part() hooks
NameDescription
triggerThe wrapper around the trigger slot.
dialogThe native modal <dialog>.
fieldThe row holding the search icon and input.
search-iconThe leading search glyph.
listboxThe role="listbox" wrapper around the slot.
emptyThe role="status" "No commands found" message.

States

Grouped items

role="group" with a label divides the list. Typing filters across every group, and a group whose items all fail the query is hidden with them.

Grouped items
Source
<hui-command label="Command menu" placeholder="Search commands">
<button slot="trigger">Open command</button>
<div role="option" data-value="new">New file</div>
<div role="group" data-group aria-label="Edit">
<div role="option" data-value="undo">Undo</div>
<div role="option" data-value="redo">Redo</div>
</div>
<div role="option" data-value="settings">Settings</div>
</hui-command>

Flat list

The same palette without groups: one flat list, filtered as you type.

Flat list
Source
<hui-command label="Command menu" placeholder="Search commands">
<button slot="trigger">Open command</button>
<div role="option" data-value="new">New file</div>
<div role="option" data-value="open">Open file</div>
<div role="option" data-value="settings">Settings</div>
</hui-command>

Server-side mechanics

The server owns the commands: it renders the trigger into the trigger slot and the options as light-DOM role="option" elements with a data-value, optionally wrapped in a role="group". It owns no filtering or selection logic; the element filters those options locally and emits hui-select with the chosen value so the host can run or authorise the action. The element owns the modal dialog, the highlight and the focus restore, so the server must not render its own dialog or listbox. An hx-swap that replaces the command while it is open removes the top-layer modal and its focus restore, so refresh the option set with an out-of-band swap rather than replacing the element.

Accessibility

  • The dialog is named by label through aria-label.
  • The search input is a role="combobox" with aria-expanded="true" and aria-controls pointing at the listbox.
  • Focus moves to the input on open and returns to the trigger on close; any element passed to show() overrides the remembered trigger.
  • Clicking the dimmed backdrop closes the palette, which a modal dialog does not do by itself.
  • When nothing matches, the role="status" empty message is shown.

Keyboard

Keyboard
KeysAction
ArrowDown, ArrowUpMove the highlight through the visible commands, wrapping at the ends.
Home, EndMove the highlight to the first or last visible command.
EnterRun the highlighted command by emitting hui-select, then close.
EscapeClose the palette and restore focus to the trigger.
Printable charactersFilter the commands and hide a group once all of its items are gone.

Gotchas

  • The open palette is a native modal <dialog> in the top layer, so a swap that replaces it while open tears the modal down and loses the trigger focus would return to.
  • Its filter helper is private; the host cannot call it and must drive filtering through the search input's own events.
  • The palette runs nothing itself; hui-select only reports the choice, so the host performs the action and any authorisation.