Early bird: 30% off Dashral ProSee pricing

Dashral

Command ButtonPro

A button that widens into a command panel: type to find an action, a small form slides into the same panel where one's needed, and the button says what happened.

Installation

When to use

  • The New or Quick add button of an app: the handful of things people create or jump to, in one place they can type into.
  • Actions that need a little input (a name, an email) without a dialog: the step opens inside the same panel.

When not to use

  • A single action: a plain button does it in one click.
  • Searching the whole app's content: a command palette over the page (Command) fits better.

Usage

Command Button with your own data: a whole component you can paste as a page.

example.tsx
"use client"

import { CommandButton } from "@/components/dashral/pro/command-button"

import { FolderPlus, Settings } from "lucide-react"

const actions = [
  {
    id: "project",
    label: "New project",
    icon: <FolderPlus />,
    group: "Create",
    // A step inside the panel: call done() with what the button should say after.
    panel: ({ done }: { done: (message?: string) => void }) => (
      <button type="button" onClick={() => done("Project created")} className="w-full rounded-md bg-foreground px-3 py-2 text-sm text-background">
        Create
      </button>
    ),
  },
  { id: "settings", label: "Settings", icon: <Settings />, group: "Go to", onSelect: () => "Opening Settings" },
]

export default function Toolbar() {
  return <CommandButton actions={actions} />
}

API reference

CommandButton

A button that widens into a command panel: type to find an action, arrows and Enter to run it, a small step (form) sliding into the same panel where an action needs one. When it's done the panel shrinks back into the button, which says what happened for a moment.

PropTypeDefault
actionsCommandAction[]

Required.

—
labelstring

The button's words; "New" by default.

"New"
iconReact.ReactNode

The button's icon; a plus by default.

<Plus />
shortcutstring[] | null

Keys that open it from anywhere on the page; ⌘J / Ctrl J by default, null for none.

["mod", "J"]
placeholderstring"Type a command…"
side"bottom" | "top"

Where it opens: down from the button (bottom, the default) or up from it (top, for a button at a screen's foot).

"bottom"
align"start" | "end"

Which edge it keeps: the button's end (default, it grows toward the start) or its start.

"end"
widthnumber

The panel's width, in px (never wider than the screen).

380
classNamestring—
classNamesCommandButtonClassNames—

type CommandAction

PropTypeDefault
idstring—
labelstring—
descriptionstring—
iconReact.ReactNode—
groupstring

Its heading in the list ("Create", "Go to"…).

—
shortcutstring[]

Its keys, shown beside it (["mod", "P"]). Shown only: wire them in your app if they should work outside.

—
keywordsstring

Words it's also found by.

—
onSelect() => void | string | Promise<void | string>

Run it. Return a short message for the button to show after ("Project created"), as is or from a promise.

—
panel(step: { done: (message?: string) => void; back: () => void }) => React.ReactNode

Or a step inside the panel (a small form): done(message?) finishes, back() returns to the list.

—

type CommandButtonClassNames

PropTypeDefault
rootstring—
buttonstring—
panelstring—

Keyboard

KeysAction
⌘JorCtrl JOpen or close it from anywhere (shortcut, null for none)
↑or↓Move through the actions
EnterRun the action, or open its step
←In a step: back to the actions (not while typing in a field)
EscBack from a step, or close; the focus returns to the button

Accessibility

  • The button says it opens a dialog and whether it's open, with its shortcut in aria-keyshortcuts.
  • The panel is a dialog (aria-modal) named Commands; its field is a combobox pointing at the highlighted action in the listbox (aria-activedescendant).
  • What an action says after ("Project created") is announced in a live region.

Motion

  • The button widens into the panel (0.42 s) and, closing, shrinks back onto it while the two crossfade there.
  • The highlight glides from action to action; a step slides in from the side as the panel reshapes to it. The button's words swap for the message, then back.
  • Smooth tweens on [0.4, 0, 0.2, 1], never springs, except the small press. With reduced motion it only fades.