Actions
Button
Button is a primary interactive trigger for user actions — submit, confirm, navigate, or initiate a process.
Style (Primary/Secondary/Danger) × Size (Small/Medium/Large) × State (Default/Hover/Pressed/Focused/Disabled) — 45 variants
stableReact AvailableFigma AvailableDocs Partial
- React last updated
- 2026-07-11
- Documentation last updated
- 2026-06-01
- Accessibility target
- WCAG 2.2 AA (target)
- Version
- 1.0.0
Known open questions
- Confirm exact control height and horizontal padding per size from Figma.
- Trailing icon is not defined as a separate Figma property on Button — supported in code for composition; confirm with design.
- UNRESOLVED DESIGN REVIEW: Primary Glass Hover dark content can appear visually muddy over high-frequency/complex backgrounds. Current #17181B content and 24% hover fill match live Figma; any readability adjustment must be approved in Figma first.
Live preview
Interactive Button instances using scoped Skrewww tokens. Shape and surface apply to the examples without changing the documentation shell.
Visual variants
Sizes
Small / Medium / Large
States
Default / Disabled / Loading
Icons & layout
Leading icon / Trailing icon / Full width
Icon foreground
Purpose
Primary interactive trigger for user actions — submit, confirm, navigate, initiate a process.
Anatomy
Button = native button or link + visual surface + content row (optional leading/trailing icon + label). Icons are consumer-supplied nodes; Button does not wrap them in SVG or change their paths.
Variants and states
primary · secondary · danger
When to use
For the main action(s) in a view. Use Primary for the single most important action, Secondary for alternative/less prominent actions, Danger for destructive actions requiring confirmation.
When not to use
For page-to-page navigation (use Link). For toggling a binary state (use Switch). For a single icon-only action (use Icon Button).
Accessibility
Renders as a real <button>, not a styled <div>. Keyboard: Tab to focus, Enter/Space to activate. Focus uses semantic/focus-ring (5.31:1 contrast, confirmed on the Accessibility page). Never rely on the Disabled state alone to explain unavailability — pair with visible explanatory text nearby.
Common mistakes
More than one Primary button in the same view (dilutes hierarchy). Not disabling the button during an async action (allows double-submission). Skipping a confirmation step for Danger actions just because the button is red.
Properties
Style × Size × State as variants. Label (text). Has Icon (boolean) + Icon (instance-swap). React icons inherit Button's CSS color via SVG currentColor — Primary/Danger follow surface-content (light on Flat/Gradient, dark on Glass); Secondary follows primary text. Button does not recolor or rewrite icon artwork.
How do Button icons get their color?
Button sets CSS color only. Primary and Danger use surface-content semantics: inverse/light on Flat and Gradient, dark on Glass. Secondary uses primary text color on every surface. Compatible icons inherit that color through SVG currentColor — Phosphor defaults fill to currentColor; stroke icons should use stroke=currentColor. Button does not set fill, stroke, or icon-specific paint, and it does not rewrite icon artwork. Any compatible icon inherits the same foreground automatically. Icon-only actions use this same Button with an aria-label; there is no separate Icon Button implementation.
Tokens used
component/radius/controlsemantic/action/primarysemantic/action/dangersemantic/surface/defaultsemantic/text/inversesemantic/text/primarysemantic/focus-ringopacity/disabled
Component API
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "primary" | "secondary" | "danger" | "primary" | Visual style mapped to semantic action tokens. |
| size | "sm" | "md" | "lg" | "md" | Control height and typography scale. |
| loading | boolean | false | Disables interaction, sets aria-busy, and shows a spinner with screen-reader text. |
| fullWidth | boolean | false | Stretches the control to the width of its container. |
| leadingIcon | ReactNode | — | Optional icon before the label (Figma: Has Icon + Icon). |
| trailingIcon | ReactNode | — | Optional icon after the label. Not confirmed as a Figma Button property. |
| href | string | — | When set, renders as a semantic link (Next.js Link or native anchor for external URLs). |
| target | string | — | Link target. `_blank` automatically adds `rel="noopener noreferrer"` when rel is omitted. |
| disabled | boolean | false | Native disabled for buttons; aria-disabled non-navigating link for href variant. |
| aria-label | string | — | Required accessible name for icon-only buttons. |
React example
Copy React example
import { Plus } from "@phosphor-icons/react";
import { Button } from "@/components/ui/Button";
export function Example() {
return (
<Button variant="primary" leadingIcon={<Plus size={16} />} type="button">
Create project
</Button>
);
}