Klarhimmel UI

Command

Stable

A search box over a list of actions, and the Cmd+K palette that puts it in a dialog.

npx shadcn add @klarhimmel/command

When to use

  • Use for

    A fast way to go anywhere or do anything by typing its name: the Cmd+K palette.

  • Not for

    Choosing one value in a form. Use Combobox. Navigation people need to see. Use Tabs or a menu.

  • Pairs with

    Dialog, Kbd, Button.

Preview

Press Ctrl or Cmd and K, or the button.

command.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Command, CommandDialog } from '@/components/klarhimmel/ui/command';

export default function Example() {
  return (
    <Theme theme="brand">
      <CommandDialog
        open
        onOpenChange={() => {}}
        title="Command menu"
        aria-label="Commands"
        hotkey="k"
        onSelect={(value) => console.log(value)}
        groups={[{ heading: 'Go to', items: [{ value: 'rooms', label: 'Rooms' }, { value: 'guests', label: 'Guests' }] }]}
      />
    </Theme>
  );
}

Install

Import

import { Command, CommandDialog } from '@/components/klarhimmel/ui/command';
Lands in
components/klarhimmel/ui/command.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
Limit
Overlays render into the Theme root with position: fixed. A transform on an ancestor re-parents fixed (a dialog centres in the wrapper) and overflow: hidden clips menus. Render <Theme> at the app root, outside any transformed wrapper.

Examples

  • Inline

    5 results

    Seaside suite
    Garden room
    Loft
    Deep tissue massage
    Sauna for two

    Type, then Up and Down, then Enter.

    import { useState } from 'react';
    import { Command } from '@/components/klarhimmel/ui/command';
    
    const groups = [
      { heading: 'Rooms', items: [{ value: 'sea', label: 'Seaside suite' }, { value: 'garden', label: 'Garden room' }, { value: 'loft', label: 'Loft', keywords: ['attic'] }] },
      { heading: 'Treatments', items: [{ value: 'massage', label: 'Deep tissue massage' }, { value: 'sauna', label: 'Sauna for two' }] },
    ];
    
    export default function CommandInline() {
      const [last, setLast] = useState('');
      return (
        <div className="flex w-[min(28rem,calc(100cqw-3rem))] flex-col gap-3">
          <div className="overflow-hidden rounded-kh-xl border border-kh-line bg-kh-surface shadow-kh-1">
            <Command aria-label="Find a room or treatment" groups={groups} onSelect={setLast} placeholder="Find a room or a treatment…" />
          </div>
          <p role="status" className="text-kh-small text-kh-muted">{last ? `Chose ${last}.` : 'Type, then Up and Down, then Enter.'}</p>
        </div>
      );
    }
  • Empty

    No help articles match. Try fewer words.

    No help articles match. Try fewer words.

    import { Command } from '@/components/klarhimmel/ui/command';
    
    export default function CommandEmpty() {
      return (
        <div className="w-[min(28rem,calc(100cqw-3rem))] overflow-hidden rounded-kh-xl border border-kh-line bg-kh-surface shadow-kh-1">
          <Command
            aria-label="Search the help centre"
            query="refund policy"
            groups={[{ heading: 'Help articles', items: [{ value: 'cancel', label: 'Cancelling a booking' }, { value: 'pay', label: 'Paying by invoice' }] }]}
            emptyText="No help articles match. Try fewer words."
          />
        </div>
      );
    }

API reference

PropTypeDefaultDescription
groupsCommandGroup[]-Required. A group is { heading?, items }; an item is { value, label, keywords?, shortcut?, icon?, disabled? }. Every word typed must match a label or a keyword. Shortcuts only remind; the app binds the keys.
onSelect(value: string) => void-Called with the item's value on Enter or click.
placeholder / emptyTextstringEnglish wordsThe box ('Type a command or search…') and the empty message ('No results'). Pass them translated.
resultsLabel(count: number) => string'3 results'What is announced as you type.
aria-labelstring-Required: names the box and the list.
CommandDialog propsopen, onOpenChange, title, hotkey-Controlled. The title names the dialog for assistive tech and is not shown. hotkey is a letter that toggles it with Cmd or Ctrl.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
Cmd or Ctrl + KOpens and closes the palette (hotkey)
TypeNarrows the list; every word must match
Down / UpMoves the highlight, wrapping; disabled items are skipped
EnterChooses the highlighted item
EscapeClears the text; again, closes the dialog

The WAI-ARIA combobox with an always-open listbox: focus stays in the box and aria-activedescendant follows the highlight. The number of results is announced as you type.

Accessibility