Button
Starts an action, like sending a message or saving a change.
Examples
Ready
Styles
primarydefault
solid
secondary
outline
ghost
destructive
link
Sizes
sm
mddefault
lg
icon-sm
icon
icon-lg
Pending
falsedefault
true
States
rest
hover
focus
active
disabled
Usage
When to use it
- Use
- The person does something with their money or account: send, pay, save, add a card.
- Use
- The view has one main action that has to be found in a second.
- Use
- A row or toolbar repeats a small action on every item (ghost, icon sizes).
- Not for
- The control moves to another page. Use a link; variant="link" is for actions that stay on the page.
- Not for
- The control turns a setting on or off at once. Use Switch.
- Not for
- The person chooses one of two to five views. Use Segmented.
- Not for
- It is a topic or filter the person toggles. Use Chip.
Rules
12 rules
- Must
- Use variant="primary" once per view; make every other action solid, secondary, outline or ghost. Because two lime buttons leave the person guessing which one moves the money.
- Must
- Set pending and keep the children; never swap the label while an action runs. Because a new label changes the width and the accessible name mid-press.
- Must
- Set type="submit" on the Button that submits a form; Base UI defaults to type="button". Because without it the Button presses and Enter in a field never submit the form.
- Must
- Use destructive only on the confirming button inside a dialog; keep the trigger outline or ghost. Because red on the trigger alarms before anything is at stake and takes the weight from the real confirm.
- Must
- Give icon-sm, icon and icon-lg an aria-label. Because the glyph is aria-hidden, so a screen reader announces only "button".
- Never
- Put white text on the lime fill; use variant="primary", which carries near-black text. Because white on the lime fails contrast in both themes.
- Never
- Add glow classes to other buttons; the glow marks primary and destructive only. Because the glow marks the next step, and a glowing Share competes with Send for it.
- Must
- Pass only placement classes in className, never fills, heights or radii. Because a hand-set fill skips the variant's hover, pressed and forced-colors states and its checked contrast.
- Should
- Say why a button is disabled in text beside it; a disabled button takes no focus or hover on touch.
- Should
- Label with a verb and its object in sentence case.
- Should
- Show a money amount in an Amount beside the button, not in the label.
- Should
- Use size="lg" (48px) for the main action on phone-first surfaces.
Accessibility
5 checks
- Focus shows a 2px olive outline on :focus-visible, on every variant.
- Name icon sizes with aria-label; the glyph is aria-hidden.
- Pending sets aria-busy and keeps the idle name.
- Report results in a role="status" region, never in the label.
- Text on the lime and on the red passes 4.5:1 at every glow stop, in both themes; Color lists each ratio, computed from tokens.css.
Edge cases
11 edge cases
- Long label
- Never wraps and never grows past its container: a text label too long for the column ends in an ellipsis, and the full label stays the accessible name.
- Row narrower than its buttons
- Buttons are shrink-0 and overflow. Use flex-wrap.
- Right to left
- The row mirrors. An arrow icon does not flip; use rtl:-scale-x-100.
- Zoom 200%
- The label grows sideways and the fixed height stays; nothing clips.
- Slow action
- Pass pending even when false so the box is as wide as the pending layer from the start.
- Double click or held Enter
- Pending blocks repeats until the parent clears it.
- Empty or unnamed icon button
- Announced as "button"; Lime has no development warning.
- Inside a form
- Submits only with type="submit".
- Inside an anchor
- Two tab stops; render the Button as the link with render.
- Forced colors
- The glow drops and every variant but link gets a 1px ButtonText border.
- Reduced motion
- No press scale; the lit layer switches without a fade; the spinner keeps turning.
API
7 props
variant'primary' | 'solid' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link'default'primary'size'sm' | 'md' | 'lg' | 'icon-sm' | 'icon' | 'icon-lg'default'md'pendingbooleaniconReactNodedisabledbooleandefaultfalserenderReactElementclassNamestring
Tokens
26 tokens
--accent- #e4f222
--on-accent- #0f0f0d
--glow-accent-from- #dbec1c
--glow-accent-to- #f6ff7c
--glow-accent-edge- rgb(255 255 255 / 0.7)
--glow-accent-bloom- rgb(255 255 255 / 0.28)
--glow-accent-cast- 112 120 20
--ink- #0f0f0d
--ink-hover- #2a2a26
--fg-inverse- #ffffff
--surface-2- #efeeea
--surface-3- #e6e4df
--line-strong- #cfcdc6
--danger- #d93636
--on-danger- #ffffff
--fg-disabled- #a6a49b
--control-sm- 32px
--control-md- 40px
--control-lg- 48px
--radius-control- 999px
--ring- var(--accent-line)
--focus-offset- 2px
--dur-instant- 80ms
--dur-base- 200ms
--ease-out- cubic-bezier(0.16, 1, 0.3, 1)
--dur-spin- 900ms