Button
Triggers an action. Pick the variant by how much attention the action deserves and the size by the surface it sits on.
Quick reference
Variants
Hover each swatch to see its interaction state.
Naming: a tone that shows at rest leads (destructive, destructive-secondary); a tone that only appears on interaction follows the base (ghost-destructive, soft-accent, outline-destructive). How a button rests — muted or rounded — is a modifier, not a variant.
| Button | MiniButton | variant | Tier | At rest → on hover | Use for |
|---|---|---|---|---|---|
accent | Primary | Accent fill → darker | The one brand-forward CTA on a screen (Buy label, Create order) | ||
default | Primary | Primary fill → lighter | The neutral primary action (Save, Pay) | ||
secondary | Secondary | White chip + border → highlight | Supporting actions beside the primary; toolbar actions | ||
outline | Secondary | Hairline → faint wash | Quieter supporting action | ||
soft | Tertiary | Neutral wash → stronger wash | Tertiary filled control; MiniButton default | ||
soft-accent | Tertiary | Neutral wash → blue tint | Additive chip (+) that hints intent on hover | ||
soft-destructive | Tertiary | Neutral wash → red tint | Remove chip (×) that hints intent on hover | ||
ghost | Tertiary | Nothing → wash | Dense toolbars, table rows, dialog Cancel | ||
ghost-destructive | Tertiary | Nothing → red tonal | Trash icon in a table row | ||
ghost-success | Tertiary | Nothing → green tonal | Affirmative row action (mark as paid) | ||
link | Tertiary | Text → underline | Action that reads as inline text | ||
destructive | Primary | Solid red | Irreversible confirm (void label, delete store) | ||
destructive-secondary | Secondary | Red tonal → stronger | Remove that sits among other actions | ||
outline-destructive | Tertiary | Hairline → red tonal + red border | Low-emphasis remove with a visible edge |
Every destructive and soft-tinted variant also shifts on keyboard focus, so the signal isn’t mouse-only. MiniButton takes the same variants and defaults to soft. Add muted to rest any non-filled variant at the muted text color.
Sizes
| Swatch | size | Box | Glyph | Use for |
|---|---|---|---|---|
xs | 24px tall | 12px | Popovers, dense inline controls | |
sm | 32px tall | 16px | Table rows, cards, compact toolbars | |
md (default) | 36px tall | 16px | Dialogs, forms, page headers | |
lg | 40px tall | 16px | Rare — a standalone hero action | |
icon-xs | 24px square | 12px | Icon-only in dense rows | |
icon-sm | 32px square | 16px | Icon-only beside sm buttons | |
icon | 36px square | 16px | Icon-only beside md buttons | |
icon-lg | 40px square | 16px | Icon-only beside lg buttons |
MiniButton sizes
A separate, text-relative scale: the box matches the line height of the text it rides next to.
| Swatch | size | Box | Glyph | Rides next to |
|---|---|---|---|---|
sm | 16px | 10px | text-xs | |
md (default) | 20px | 12px | text-sm (default UI text) | |
lg | 24px | 14px | text-base | |
xl | 28px | 16px | text-lg |
Which button component?
| Need | Use |
|---|---|
| Run an action on click | Button |
| Navigate to an in-app route | ButtonLink — a real <a> with Button’s variants |
| External link or file download styled as a button | Button with render={<a href="…" />} |
| Tiny ×, +, −, or trash next to text (chip, list row, toast) | MiniButton |
| Async one-shot action that shows pending → success/error in place | ActionButton |
| A label that opens a menu or popover (with chevron) | DisclosureButton |
| Several related buttons joined into one bar | ButtonGroup |
Recipes
Dialog footer
Default size. Cancel on the left as ghost, the primary action on the right. Label them Cancel and Save (not “Apply”). AlertDialogCancel renders outline by default.
Popover actions
Popovers step down to size="xs".
Table row actions
Keep rows quiet: sm for a labeled action; for icon-only ones, a muted ghost variant — ghost, ghost-success, or ghost-destructive.
Link or download styled as a button
For in-app navigation use ButtonLink. For an external URL or a file download, render the button as an anchor:
Async action
For a one-shot action that should show pending → success/error in place, use ActionButton. For a submit that just blocks while pending, pass loading (see Loading).
Choosing emphasis
Variants are a ladder, not a palette. Give a view one primary action and step everything else down, so the primary stays the obvious payoff.
- Primary (
default,accent,destructive) — one per view. Reach foraccentonly for a clearly affirmative, brand-forward action, and keep it to one per view. - Secondary (
secondary,outline,destructive-secondary) — “not the main thing, but still a real button.” - Tertiary (
soft*,ghost*,outline-destructive,link) — frequent, repeated, or inline actions that shouldn’t compete for attention.
With icons
Mark a leading or trailing icon with data-icon="inline-start" / "inline-end" so the padding tightens on that side.
Icon only
Use an icon* size and always pass an aria-label.
Modifiers
Boolean props that combine with any variant.
Rounded
Add rounded to fully round any button — a pill for text, a circle for icon-only. For small text-scaled chips, use MiniButton instead of hand-sizing an icon button.
Muted
muted rests the label or icon at the muted text color; the variant’s own hover and focus colors still apply. Use it for quiet, repeated controls — clear buttons inside inputs, icon actions in table rows — so they recede until reached for. It has no effect on filled variants (default, accent, destructive, destructive-secondary), which set their text against the fill.
MiniButton
MiniButton is a small, text-scaled icon button — the ×, +, −, or trash that rides next to text (a chip, list row, dialog, or toast). It’s a <Button rounded> preset on a text-relative size scale (MiniButton sizes), distinct from Button’s control-row sizes. Pass the glyph as children; pick emphasis with variant (defaults to soft). Always pass an aria-label.
In a row — the label owns the space, the button rides the trailing edge and reddens on hover:
Loading
loading shows a spinner and disables the button until it clears.
Disabled
Props
Also accepts every native <button> attribute (type, onClick, aria-*, …).
| Prop | Type | Default | Description |
|---|---|---|---|
variant | see Variants | "default" | Emphasis and interaction style |
size | see Sizes | "md" | Height and padding, or square box for icon* sizes |
rounded | boolean | false | Fully round — pill for text, circle for icon-only |
muted | boolean | false | Rest at the muted text color; hover/focus colors unchanged. No effect on filled variants |
loading | boolean | false | Shows a spinner and disables the button |
disabled | boolean | false | Disables interaction and dims the button |
render | ReactElement | <button> | Swap the rendered element (e.g. <a href>) while keeping button styling |
className | string | — | Merged onto the button; prefer a variant or size over restyling |