# Command

> A command palette: a search box over grouped actions with shortcuts, keyboard-first, inline or as a Ctrl+K dialog.

Category: Overlays. Docs page: https://rime.mjsons.net/components/command

## Installation

```bash
npx shadcn@latest add KezLahd/rime/command
```

Command is a combobox over a grouped listbox. Typing filters every group (every word must appear in the label or the item's keywords), Up and Down move the highlight, Enter runs it, and an empty state says when nothing matches. Focus stays in the input throughout; the highlighted option is announced through aria-activedescendant.

CommandDialog lifts it over the page in the modal glass and toggles it with Ctrl+K or Cmd+K (shortcut, on by default). Running a command closes it; Esc and the scrim close it too, and focus returns to where it was.

inline renders the open panel in the page flow: no portal, scrim, focus trap, scroll lock or outside-click listener. It exists for docs, previews and static mock-ups; never ship it as the live overlay.

## Usage

```tsx
import { Command, CommandDialog } from "@/components/ui";

<CommandDialog
  open={open}
  onOpenChange={setOpen}
  groups={[
    { heading: "Suggestions", items: [{ id: "invoice", label: "Create invoice", shortcut: "Ctrl N", onSelect: create }] },
    { heading: "Go to", items: [{ id: "orders", label: "Orders", onSelect: () => router.push("/orders") }] },
  ]}
/>
```

## Props: Command

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `groups` (required) | `ReadonlyArray<CommandGroup>` |  |  |
| `label` | `string` | `"Search commands"` | The input's accessible name. |
| `placeholder` | `string` | `"Type a command or search…"` |  |
| `emptyText` | `ReactNode` | `"No results found."` | Shown when nothing matches. |
| `onSelect` | `(item: CommandItem) => void` |  | Called with the item after its own onSelect runs. |
| `query` | `string` |  | Controlled query. Leave unset for uncontrolled. |
| `onQueryChange` | `(q: string) => void` |  |  |
| `autoFocus` | `boolean` |  | Focus the input on mount. |
| `className` | `string` |  |  |

## Props: CommandDialog

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` |  | The input's accessible name. |
| `className` | `string` |  |  |
| `onSelect` | `(item: CommandItem) => void` |  | Called with the item after its own onSelect runs. |
| `placeholder` | `string` |  |  |
| `groups` (required) | `ReadonlyArray<CommandGroup>` |  |  |
| `emptyText` | `ReactNode` |  | Shown when nothing matches. |
| `query` | `string` |  | Controlled query. Leave unset for uncontrolled. |
| `onQueryChange` | `(q: string) => void` |  |  |
| `open` (required) | `boolean` |  |  |
| `onOpenChange` (required) | `(open: boolean) => void` |  |  |
| `shortcut` | `boolean` | `true` | Toggle with Ctrl+K or Cmd+K from anywhere on the page. On by default. |
| `inline` | `boolean` |  | Render the open palette in the page flow: no portal, scrim, focus trap or shortcut (docs, previews). |

## Examples

### Palette

Type to filter; arrows and Enter run a command.

```tsx
<Command groups={GROUPS} label="Search commands" />
```

### As a dialog with Ctrl+K

Press the button, or Ctrl K anywhere on this page.

```tsx
<CommandDialog open={open} onOpenChange={setOpen} groups={GROUPS} />
```

## Theme tokens

Per-component tokens. Undeclared by default: the default below is the fallback the CSS uses. Set any of them at :root or on a wrapper to restyle every instance.

| Token | Default |
| --- | --- |
| `--command-dialog-width` | `560px` |
| `--command-input-h` | `48px` |
| `--command-list-max` | `320px` |
| `--command-radius` | `var(--r-lg)` |
| `--command-width` | `440px` |
| `--popover-item-active-bg` | `linear-gradient(to right, rgba(var(--rgb-brand), 0.14), rgba(var(--rgb-brand-deep), 0.07))` |
| `--popover-item-radius` | `var(--r-xs)` |
| `--popover-pad` | `calc(6px * var(--density))` |

System tokens it reads: `--border-w`, `--density`, `--dur`, `--dur-slow`, `--ease`, `--ease-out`, `--font-body`, `--ink-body`, `--ink-brand`, `--ink-disabled`, `--ink-heading`, `--ink-muted`, `--ink-placeholder`, `--line`, `--line-glass`, `--line-soft`, `--modal-blur`, `--modal-scrim`, `--modal-scrim-blur`, `--modal-shadow`, `--modal-surface`, `--popover-blur`, `--popover-border`, `--popover-shadow`, `--popover-surface`, `--r-2xs`, `--r-lg`, `--r-xs`, `--rgb-brand`, `--rgb-brand-deep`, `--rgb-white`, `--text-caption`, `--text-input`, `--text-micro`, `--text-small`, `--tracking-label`, `--z-modal`.

## Accessibility

- The input is role="combobox" with aria-controls and aria-activedescendant; results are role="listbox" with role="group" sections labelled by their headings and role="option" rows.
- The result count is announced through a polite status region as you type.
- CommandDialog is a modal dialog: focus moves to the input, Tab is trapped, Esc closes, focus returns.

## Do

- Add keywords for the words people actually type ("new", "add").
- Show the real shortcut beside each command that has one.

## Don't

- Don't make the palette the only way to reach a page.
- Don't fill it with every possible action; keep the top group to what is likely now.

Related: https://rime.mjsons.net/components/nav-search, https://rime.mjsons.net/components/menu
