AuroraUI. Components. Command
(Aurora UI v0.1.0)
View Source
Command family — search field, search results, and the command palette.
These components cover the "find and act" surface of an app: a debounced search input, an accessible results list with a live result-count announcement and an empty state, and a command palette opened from a visible trigger (never a keyboard shortcut alone).
Semantics
Search is server-rendered: the input emits phx-change/phx-submit and the
server re-renders search_results/1. JavaScript only enhances — the palette's
filtering/keyboard behavior is layered on top of a fully working button +
form. Results are a semantic list (<ul role="list">), not a listbox:
results are navigable content (links), not options in a single-select widget,
so the list-plus-link semantics match assistive-technology expectations and
keep native link keyboard behavior. The palette is the one exception — it is a
filter-then-pick widget, so it uses the combobox + listbox pattern.
Debounce guidance
Always debounce the live search so you do not round-trip on every keystroke.
Set debounce (renders phx-debounce); 120–300 ms feels responsive
without flooding the server, and "blur" defers until focus leaves. Pair a
phx-change for incremental results with a phx-submit fallback so pressing
Enter still works when JS is unavailable.
Summary
Functions
A command palette opened from a visible trigger button.
A search input inside a role="search" landmark.
A single result row. Becomes a link when navigate/patch/href is set,
otherwise a static <li> (wire phx-click for command-style rows).
A results list with a polite result-count announcement and an empty state.
Functions
A command palette opened from a visible trigger button.
The palette is a dialog (role="dialog", aria-modal, labelled by its title)
containing a combobox search input and grouped commands in a listbox.
Enhancement is lazy: phx-hook="AuroraCommandPalette" dynamic-imports
./command.js only where a palette actually renders, keeping it out of every
other bundle.
Shortcuts are an enhancement, never the only door
The visible trigger is the source of truth, so the palette works with zero
keyboard shortcuts. When you do wire one (via shortcut), it must be:
- discoverable — shown as a
kbdhint on the trigger and inside the dialog, so users can learn it; - configurable — the value is an attribute, not hard-coded;
- non-hijacking — the hook must never intercept browser or assistive-technology shortcuts (it listens for the configured combo only, and never swallows keys while focus is in another input or AT is driving).
Examples
<.command_palette id="cmdk" shortcut="⌘K">
<:group label="Navigation">
<button role="option" data-aui-command-item phx-click="go" phx-value-to="/inbox">
Go to Inbox
</button>
</:group>
<:empty>No commands match.</:empty>
</.command_palette>Attributes
id(:string) - stable id — required for the hook to survive patches. Defaults tonil.label(:string) - dialog accessible name (title). Defaults to"Command palette".trigger_label(:string) - visible trigger button text. Defaults to"Search commands".placeholder(:string) - Defaults to"Type a command or search…".open(:boolean) - server-controlled open state. Defaults tofalse.shortcut(:string) - discoverable, configurable shortcut hint (e.g. "⌘K"); shown as a kbd and passed to the hook. Defaults tonil.- Global attributes are accepted.
Slots
group- a titled group of commands; place role=option items inside. Accepts attributes:label(:string) (required)
empty- shown when the filter matches nothing.
A search input inside a role="search" landmark.
Renders type="search" + explicit role="searchbox", a leading search icon,
an optional clear button (auto-hidden while empty via :placeholder-shown),
and a busy state (aria-busy) with a spinner. Emits phx-change/phx-submit
from the wrapping form.
Examples
<.search_field phx-change="search" phx-submit="search" debounce={200} value={@q} />
<.search_field label="Find a doc" loading={@searching} name="query" />Attributes
id(:string) - stable id; generated when omitted. Defaults tonil.name(:string) - input name submitted with the form. Defaults to"q".value(:string) - current query value. Defaults tonil.label(:string) - accessible name for the search region and input. Defaults to"Search".placeholder(:string) - Defaults to"Search…".size(:string) - Defaults to"md". Must be one of"sm","md", or"lg".loading(:boolean) - sets aria-busy and shows a spinner. Defaults tofalse.clearable(:boolean) - renders a clear button when the field has text. Defaults totrue.debounce(:any) - renders phx-debounce (ms integer or "blur"); always debounce live search. Defaults tonil.- Global attributes are accepted. Supports all globals plus:
["phx-change", "phx-submit", "phx-target", "phx-value-id", "autocomplete", "method", "action"].
Slots
inner_block- optional trailing controls rendered after the input (e.g. a scope select).
A single result row. Becomes a link when navigate/patch/href is set,
otherwise a static <li> (wire phx-click for command-style rows).
Attributes
navigate(:string) - Defaults tonil.patch(:string) - Defaults tonil.href(:string) - Defaults tonil.active(:boolean) - highlights the current/keyboard-focused row. Defaults tofalse.- Global attributes are accepted. Supports all globals plus:
["phx-click", "phx-value-id", "target", "rel", "download"].
Slots
icon- leading media/icon (decorative).meta- trailing metadata (shortcut, type, timestamp).inner_block(required) - the result title/summary.
A results list with a polite result-count announcement and an empty state.
When count is 0 the empty slot renders (a default message otherwise);
any other value renders the results. The count is announced through a
visually-hidden aria-live="polite" region so screen-reader users hear how
many matches came back without moving focus. Use the group slot for titled
sections, or the default slot for a flat list of search_result/1.
Examples
<.search_results count={length(@results)}>
<.search_result :for={r <- @results} navigate={~p"/docs/#{r.slug}"}>
{r.title}
</.search_result>
<:empty>
No matches. Try <.search_result>installation</.search_result>.
</:empty>
</.search_results>Attributes
id(:string) - Defaults tonil.label(:string) - accessible name for the results list. Defaults to"Search results".count(:integer) - number of results; drives the aria-live announcement and the empty state. nil = unknown. Defaults tonil.loading(:boolean) - Defaults tofalse.- Global attributes are accepted.
Slots
group- an optional titled group of results. Accepts attributes:label(:string) (required)
inner_block- flat results (search_result/1) when not grouping.empty- no-results content; put suggested queries/actions here.