Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

Button

A button for actions: four variants, three sizes, a loading state, icons and a navigation arrow.

import { Button } from "@butteryui/react";

Demo

When to use it

Use it for

  • Actions: save, export, add, open a dialog
  • Steps forward and back with arrow="forward" or arrow="back": Next, Back, Continue, Get started
  • A link that should look like a button, with asChild
  • A main action with its keyboard shortcut shown after the label, with shortcut="n"

Not for

  • An action shown only as an icon: use IconButton
  • A link inside text or a quiet 'See all': use ArrowLink
  • Picking one of a few options: use SegmentedControl
  • A main action with a few alternatives: use SplitButton

Examples

Add to order

One action and what it changes: each press adds a bag to the order.

0 bags in your order

A link dressed as a call to action, with the forward arrow.

Save and cancel

A form's closing pair: the main action in primary, the way out in soft.

You have unsaved changes

Saving

A save that takes a moment: the button shows a spinner and holds its width until it's done.

Not saved yet

Step buttons

Back and Next with arrows in equal columns; the last step finishes, so its button drops the arrow.

Pick a coffee

Variants and sizes

The four variants, from the page's main action to its quietest, and the three sizes.

Last action: Nothing yet

Variants and states

Variants

Primary for the one main action in view, soft for the rest, secondary on tinted surfaces, ghost for quiet actions.

Sizes and shapes

28, 36 and 48px tall; rounded corners for dialog steps.

Arrows

Forward and back steps. Hover or focus one: the chevron grows a stem and the button keeps its width.

Icons

With a shortcut

The key that does the same thing, after the label in the button's own color. Bind it with useShortcut.

Loading

A spinner over the label; the button keeps its size and ignores presses.

Disabled

Pass aria-disabled for an action that isn't available yet, such as Save before a form is complete or Back on a first step: the button looks like this and ignores presses but stays in the Tab order, so focus doesn't drop to the page when it turns off and screen readers still find it. Pass disabled only for an action that stays off for as long as the screen is open, such as one the person has no permission for.

Props

Button

PropTypeDefaultDescription
variant"primary" | "secondary" | "soft" | "ghost""soft""primary" for the one main action in view, "soft" (the default) for the rest, "secondary" on a tinted surface such as the stage, and "ghost" for quiet actions in toolbars.
size"sm" | "md" | "cta""md""sm" (28px), "md" (36px, the default) or "cta" (48px, for dialog steps and calls to action).
shape"pill" | "rounded""pill""pill" (the default) or "rounded", with 12px corners, for calls to action in dialogs.
loadingbooleanShows a spinner over the label, keeping the button's size, and ignores presses while it's busy.
iconStartComponentType<IconComponentProps>An icon before the label, such as Plus from lucide-react.
iconEndComponentType<IconComponentProps>An icon after the label.
arrow"forward" | "back"A navigation arrow: "forward" after the label for Next, Continue and Get started, "back" before it. Its chevron grows a stem on hover or focus. Leave it off Done, Save, Submit and destructive actions.
shortcutShortcutA keyboard shortcut to show after the label, written as useShortcut takes it, such as "n" or "mod+n". It's announced through aria-keyshortcuts; bind it with useShortcut.
asChildbooleanRender the single child element (such as a link) as the button, with the button's look and parts merged in. The child's own content is the label.

It also takes ComponentPropsWithRef<"button">.

Accessibility

A native button, or the element you give it with asChild, named by its label; type="button" by default. loading sets aria-busy and aria-disabled and ignores presses, keeping focus where it is. aria-disabled on its own does the same without the spinner: the button stays in the Tab order and screen readers announce it as unavailable, while disabled takes it out of the Tab order, so keyboard and screen-reader users pass it without hearing it. A shortcut is announced through aria-keyshortcuts; bind it with useShortcut. Icons, the arrow and the spinner are hidden from assistive technology.

KeysWhat they do
Enter or SpacePresses it, unless it's loading or aria-disabled
EnterFollows the link, when it's a link with asChild

Motion

It scales to 97% while it's pressed, over `--bt-dur-press`, and springs back when it's let go.

With reduced motion: It presses in at once, and an arrow grows its stem at once, without the nudge.

Layout

Its height is its size's token: --bt-h-sm (28px), --bt-h-button (36px) or --bt-h-cta (48px). It's as wide as its label and padding, never wider than its container, where a long label truncates with an ellipsis and shows in full as its title. The whole pill is the hit area, and loading keeps its size.

See it in the roastery demo's overview and settings. See it in the Halfmoon shop demo. See it in the Deckhand demo.