Button

The action. Six variants and four sizes, with icons, a loading state and a held state, on the same squircle corners as Input. Built on the vendored Base UI button; ButtonLink gives a link the same look.

Playground

Button
State

Variants

Five variants, inline or full width — add className="w-full" to stretch one to its container.

Sizes

xs 24px inside dense rows and tables, sm 32px, md 36px by default, lg 40px for the page's main action — the nav's CTA. Each has a square icon-only twin.

The touch floor: on a touch screen nothing is under 40px. sm and md grow to 40px tall (and their icon-only twins to 40px square); xs keeps its look and reaches 40px through an invisible hit area, so leave it room. A mouse sees the sizes above.

With icons

Loading & disabled

Loading shows a spinner in the leading place, keeps the button focusable and ignores presses; disabled dims it. Press the first one.

As a link

A link stays a link: ButtonLink renders an <a> with the same variants, sizes and icons — never Button with a link inside.

On dark ground

Over footage or a dark photo — the landing hero — the action is inverse: white with ink words, the primary's mirror. The glass secondary turns grey there, and ink doesn't carry.

API reference

PropTypeDefaultDescription
variant"primary" | "secondary" | "ghost" | "destructive" | "danger" | "inverse""primary"Primary is the one action, black with white words; secondary white glass with a hairline; ghost no fill until hover; destructive the one red, for actions that can't be taken back; danger red words on a red wash, for the way into one (Delete account opens its confirm); inverse white with ink words, the one action on dark ground (the landing hero's footage).
size"xs" | "sm" | "md" | "lg""md"24, 32, 36 or 40px tall; on a touch screen sm and md are 40px, and xs reaches 40px through its hit area.
leadingIconIcon—An SVG icon component drawn before the label.
trailingIconIcon—An SVG icon component drawn after the label.
iconOnlybooleanfalseSquare, showing only leadingIcon. Requires aria-label.
loadingbooleanfalseSpinner in the leading place; stays focusable, ignores presses, sets aria-busy. Button only.
activebooleanfalseHolds the pressed look — e.g. while the menu it opened is showing. Button only.
disabledbooleanfalseDisables and dims the button. Button only.
classNamestring—Layout only — w-full, margins. Never restyle a variant here.

Everything else passes through to the Base UI button (type, onClick, …) or, for ButtonLink, the <a> (href, target, …). For a custom element use buttonStyles().

Guidelines

  • One primary per view: the black button is the single thing we most want pressed. Everything else steps down to secondary or ghost. Okami's actions are neutral; colour is the studio's, on its mark and ball.
  • Destructive only confirms something that can't be taken back — cancelling a booking, removing a person — and sits beside a ghost way out, never alone.
  • Labels are a verb and its object, in sentence case: “Book a slot”, “Save changes”. While loading, say what's happening (“Saving”) rather than blanking the label.
  • An icon's side is a step tighter than the label's (md 12px to 16px): a glyph sits inside its box, so equal padding reads wider beside it. Each icon marks its side (data-icon) and the size does the rest; don't pad around it by hand.
  • Icon-only buttons always carry an aria-label; reach for them only where the icon is unmistakable (search, close, add).
  • Navigation is a link: use ButtonLink. Base UI's Button enforces button semantics and must not wrap an <a>.
  • On touch-first surfaces use md or lg; keep xs for pointer-dense places like table rows, where a slip costs nothing.