Skip to content

Button

Hero — hover or focus any button

All seven states — click a chip to see each one

When to use

<Button> is Matter's canonical button primitive. Reach for it for any clickable affordance — CTAs, form submissions, navigation triggers, dialog openers. The five variants carry distinct visual weight; the seven sizes carry distinct geometric weight. Pick variant by hierarchy, size by surface density.

Do — use asChild to wrap a <Link> when the button is navigation: <Button asChild><Link href="/x">Go</Link></Button>.
Don't — hardcode button colours. Wrap the button (or its ancestor section) in a .bloom-* / .glow-* class so --btn-glow inherits correctly.

Anatomy

A single root element (a <button> by default, or whatever the asChild slot wraps) with .btn plus one .btn-{variant} plus one .btn-{size} class. Optional icon and trailing slots render before/after children respectively. Each variant carries a V1 + V2 class alias (e.g. .btn--pri, .btn-primary { … }) so <MatterButton> and <Button> share one CSS recipe.

Variant matrix

VariantUseBackground
primaryDefault CTA — liquid-glass refractive surfacePainterly white→peach gradient
outlineSecondary action sitting beside the primaryTranslucent white + stone border
ghostTertiary action — navs, inline triggers, dense chromeTransparent + tone-cascade hover
darkHigh-contrast CTA on warm surfaces (footers, blog hero)Solid #171717
stoneMuted CTA on cool surfacesSolid --stone-100

Size matrix

SizeUseGeometry
xsToolbar / row-action buttonsSquared, 8px radius
smDense tables and side railsSquared, --btn-radius-sm
mdDefault — most product surfacesSquared, --btn-radius-md
lgDisplay surfaces, hero CTAs in chrome-heavy contextsSquared, --btn-radius-lg
sm-pillMarketing-surface compact CTAPill — --radius-pill
md-pillMarketing-surface default CTAPill — --radius-pill
lg-pillMarketing-surface hero CTAPill — --radius-pill

The pill sizes share heights and padding with their squared counterparts — only the border-radius differs.

Polymorphism — asChild vs as

Two polymorphism paths are supported. Prefer asChild (Radix Slot pattern):

<Button asChild variant="dark" size="lg-pill">
  <Link href="/get-started">Get started</Link>
</Button>

as is kept for back-compat — it accepts an ElementType and renders that tag directly. When both are set, asChild wins.

Props

PropTypeDefaultDescription
variantButtonVariant"primary"
sizeButtonSize"md"
asChildbooleanRadix Slot pattern — merges .btn classes onto the only child element.
asReact.ElementTypeLegacy ElementType prop; prefer `asChild`. Ignored when asChild is true.
iconReact.ReactNode
trailingReact.ReactNode
childrenReact.ReactNode

Also accepts Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children">.

States

Density

Themes

Light / dark / forced-colors side-by-side.

Tokens consumed

No tokens match.

Accessibility

  • Keyboard interactions
  • ARIA roles
  • Focus ring uses the canonical peach ring
  • prefers-reduced-motion collapses motion to opacity

Do / Don't

A short positive example.
A short negative example.

Recipes

Where this component shows up in real screens — link to /recipes/* pages.

Source

packages/components/src/Button

On this page