Showcase component
Button
A single action control with three emphasis levels. Variants, sizes, and states follow the Figma Button masters.
Overview
Button is the action control for the system. One component covers filled, outlined, and text emphasis so hierarchy stays consistent across screens.
When to use
- The user triggers an action: continue, save, apply, cancel.
- Primary for the main action on a view.
- Secondary for a supporting choice next to primary.
- Tertiary for a low-emphasis text action.
When not to use
- Do not use a button for navigation between pages. Use a link (
<a href>). - Do not place two primary buttons in the same view. They compete for the same action.
Playground
The live control is the implemented component. Filled Lg uses the 335px canvas width from the primary master. Sm hugs content with a 136px minimum.
<Button>
Continue
</Button>Variants
Primary is filled, secondary is outlined, tertiary is text. Light and dark resolve the same token names to different primitives.
Theme
Sizes
Primary and secondary use Sm and Lg. Tertiary adds Md. Passing md on a filled button maps to Sm — that size is not in the filled masters.
Icon slots
Icons are optional slots. Show a leading icon or a trailing icon — not both on the same control.
States
Hover and pressed share fills on primary and secondary. Tertiary hover and pressed match default in Figma. Focus is a keyboard ring and is not a Figma variant.
primary
Default
Hover
Pressed
Focus
Disabled
secondary
Default
Hover
Pressed
Focus
Disabled
tertiary
Default
Hover
Pressed
Focus
Disabled
Anatomy
Container
Pill chassis. Primary fills; secondary uses a 2px inside stroke. Lg canvas width is 335px.
Leading icon
Optional. Size follows the button size token.
Label
Averta Semibold. Names the action.
Container
No fill, radius, or border. Hugs the content.
Trailing icon
Optional leading or trailing slot.
Label
Uses the link color token.
Usage
- One primary action per view. Pair it with secondary when needed.
- Label the action the user takes, not the component: Continue, not Primary.
- Keep tertiary for low-emphasis actions that should read as text.
One primary action
Pair one primary with a secondary when there is a supporting choice.
Two primaries
Two filled buttons compete. Cancel should not use primary.
Tertiary for a low-emphasis action
Use tertiary when the action should read as text, not a chassis.
Primary for a text action
Do not use the filled chassis for a low-emphasis action.
Tokens
Components consume semantic aliases. See Color and Typography for the foundation tokens those aliases point at.
Platform reference
Web names are the CSS variables used in this repo. iOS and Android strings are a naming convention for this showcase, not verified native exports.
| Figma | Web |
|---|---|
link/link-primary | var(--color-link-primary) |
link/link-primary-disabled | var(--color-link-primary-disabled) |
buttons/btn-primary-bg-default | var(--color-btn-primary-bg-default) |
buttons/btn-primary-bg-hover | var(--color-btn-primary-bg-hover) |
buttons/btn-primary-text | var(--color-btn-primary-text) |
buttons/btn-secondary-border | var(--color-btn-secondary-border) |
buttons/btn-secondary-text | var(--color-btn-secondary-text) |
buttons/btn-secondary-bg-hover | var(--color-btn-secondary-bg-hover) |
| Figma | Web |
|---|---|
radius/pill | var(--radius-pill) |
opacity-40 | var(--opacity-40) |
Icons/icon-sm | var(--size-icon-sm) |
Icons/icon-lg | var(--size-icon-lg) |
Icons/icon-xl | var(--size-icon-xl) |
border/width/semibold | var(--stroke-width-semibold) |
Unbound Figma values kept on the component: filled height 55px on Lg, filled label 15px on Lg. Showcase width for filled Lg is 335px.
Interaction and motion
- Hover and pressed use the same hover fill on primary and secondary.
- Tertiary hover and pressed match default.
- Color, opacity, and the focus ring transition in 150ms. Figma defines no motion; this is web state feedback.
prefers-reduced-motionremoves those transitions.
Accessibility
- Rendered as a native
button. - Visible
:focus-visiblering using the link token. Focus is not a Figma variant. - Disabled uses the native disabled attribute.
- Filled disabled uses 40% opacity, matching Figma. That is not a contrast guarantee.
- If you pass only an icon, provide
aria-label. Icon-only is not a Figma variant.
API
| Property | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "tertiary" | "primary" | Emphasis level. |
size | "sm" | "md" | "lg" | filled "lg", tertiary "sm" | Filled md maps to sm. |
leadingIcon | ReactNode | — | Optional icon before the label. |
trailingIcon | ReactNode | — | Optional icon after the label. |
disabled | boolean | false | Native disabled state. |
children | ReactNode | — | Visible label. Required unless aria-label is set. |
Code
<Button variant="primary" size="lg">
Continue
</Button>
<Button variant="secondary" size="lg">
Cancel
</Button>
<Button variant="tertiary" trailingIcon={<ChevronIcon />}>
Learn more
</Button>