Command
Fast, composable command menu on bits-ui Command. Fuzzy-filter as you type, arrow-key through results, Enter to commit — or wrap in a Dialog for a ⌘K palette.
Usage
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
export const commandVariants = tv({
base: 'flex size-full flex-col overflow-hidden rounded-(--radius-panel) p-1 text-popover-foreground',
variants: {
variant: {
default: '',
/* inverted matches dropdown-menu/select-content recipe — CSS-var swap so child
* primitives (command-item hover, indicator, etc.) inherit foreground-on-background. */
inverted:
'shadow-2xl ring-1 ring-background/10 dark:ring-background/15 [--popover:var(--foreground)] [--popover-foreground:var(--background)] [--accent:color-mix(in_oklch,var(--background)_12%,transparent)] [--accent-foreground:var(--background)] [--border:color-mix(in_oklch,var(--background)_15%,transparent)]'
}
},
defaultVariants: {
variant: 'default'
}
});
export type CommandVariant = VariantProps<typeof commandVariants>['variant'];
</script>
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Command as CommandPrimitive } from 'bits-ui';
import { getSurfaceLevel, setSurfaceContext, surfaceBgClass } from '$lib/components/ui/surface';
export type CommandRootApi = CommandPrimitive.Root;
let {
api = $bindable(null),
ref = $bindable(null),
value = $bindable(''),
class: className,
variant = 'default',
...restProps
}: CommandPrimitive.RootProps & {
api?: CommandRootApi | null;
variant?: CommandVariant;
} = $props();
// Track the floating panel's resolved level (Popover/Combobox already re-provided).
const level = getSurfaceLevel();
setSurfaceContext(level);
</script>
<CommandPrimitive.Root
bind:this={api}
bind:value
bind:ref
data-slot="command"
data-substrate={level}
data-surface-level={level}
data-variant={variant}
class={cn(commandVariants({ variant }), surfaceBgClass[level], className)}
{...restProps}
/>
Inverted
Set variant="inverted" on the root for a dark surface over
a light app — Bloom's signature command identity. Surface and accent CSS vars swap at the root,
so item rows, input, separators, and indicators flip contrast automatically.
<Command.Root variant="inverted" class="w-full max-w-md">
<Command.Input placeholder="Search…" />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
<Command.Group heading="Suggestions">
<Command.Item>Profile</Command.Item>
<Command.Item>Billing</Command.Item>
<Command.Item>Settings</Command.Item>
</Command.Group>
</Command.List>
</Command.Root>With icon
Drop a Remix icon (the <Icon> primitive) as the
first child of Command.Item. Spacing is set by the
item's flex layout.
<Command.Root class="w-full max-w-md rounded-xl border border-border">
<Command.Input placeholder="Search…" />
<Command.List>
<Command.Group heading="Tools">
<Command.Item><Icon name="calendar-line" /><span>Calendar</span></Command.Item>
<Command.Item><Icon name="calculator-line" /><span>Calculator</span></Command.Item>
</Command.Group>
</Command.List>
</Command.Root>With shortcut
Command.Shortcut is a right-aligned hint slot — pair it with
a leading icon for the canonical command-row shape.
<Command.Item>
<Icon name="user-line" />
<span>Profile</span>
<Command.Shortcut>⌘P</Command.Shortcut>
</Command.Item>In dialog (⌘K palette)
Wrap Command in Dialog for a centered command palette. Use p-0 on the dialog content so the command's own padding stands.
<script lang="ts">
let dialogOpen = $state(false);
</script>
<Button variant="outline" onclick={() => (dialogOpen = true)}>
<Icon name="search-line" /> Search… <Command.Shortcut>⌘K</Command.Shortcut>
</Button>
<Dialog.Root bind:open={dialogOpen}>
<Dialog.Content class="overflow-hidden p-0">
<Command.Root>
<Command.Input placeholder="Type to search…" />
<Command.List>
<Command.Empty>No results.</Command.Empty>
<Command.Group heading="Suggestions">
<Command.Item>Profile</Command.Item>
<Command.Item>Billing</Command.Item>
</Command.Group>
</Command.List>
</Command.Root>
</Dialog.Content>
</Dialog.Root>Empty
Command.Empty renders only when the active filter yields
zero results. Type into the input below to see it appear.
<Command.Root class="w-full max-w-md rounded-xl border border-border">
<Command.Input placeholder="Search a thing that doesn't exist…" value="zzz" />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
</Command.List>
</Command.Root>Command.Root props
Inherits bits-ui Command.Root props via spread.
| Prop | Type | Default | Description |
|---|---|---|---|
'default' | 'inverted' | 'default' | Surface treatment. Inverted swaps popover/accent CSS vars at the root so all child rows, input, and indicators flip contrast. | |
string (bindable) | '' | The currently highlighted item value. Two-way bindable. | |
CommandRootApi | null (bindable) | null | Bind to access the underlying command instance for programmatic control. | |
(value: string, search: string, keywords?: string[]) => number | — | Custom scoring function. Return >0 to include, 0 to filter out. | |
boolean | true | Disable to own filtering entirely (e.g. server-driven search). |
Command.Item props
| Prop | Type | Default | Description |
|---|---|---|---|
string | — | Unique key used by the filter. Defaults to the item label text. | |
'rect' | 'pill' | 'rect' | Visual variant controlling item appearance and spacing. | |
(value: string) => void | — | Fires when the item is committed via Enter or click. | |
boolean | false | Skips the item in filtering and keyboard navigation. |