Button
The foundational interactive element. Every variant, size, and state is defined here and serves as the template for all future component entries.
Preview
Sizes
Three sizes cover the full range of UI contexts: compact lists, standard forms, and prominent hero actions.
With Icons
Icons amplify intent. Use a left icon to set context, a right icon to suggest directionality.
States
All interactive states are handled. Loading replaces content with a spinner; disabled dims and removes pointer events.
Implementation
Full implementations for every platform. The React version uses a forwardRef pattern for ref forwarding. The SwiftUI version uses a custom press-state modifier for spring feedback.
import { Button } from "@/components/ui/Button";
import { ArrowRight, Download } from "lucide-react";
// Primary — the default call-to-action
<Button variant="primary">Get Started</Button>
// Secondary — for supporting actions
<Button variant="secondary">Learn More</Button>
// Ghost — for low-emphasis, inline actions
<Button variant="ghost">Cancel</Button>
// Danger — destructive actions
<Button variant="danger">Delete Account</Button>
// Glass — for overlays and hero sections
<Button variant="glass">Explore</Button>
// With icons
<Button leftIcon={<Download className="w-4 h-4" />}>
Download
</Button>
<Button rightIcon={<ArrowRight className="w-4 h-4" />}>
Continue
</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
// Loading state
<Button loading>Saving...</Button>
// Disabled
<Button disabled>Unavailable</Button>Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "ghost" | "danger" | "glass" | "primary" | Controls the visual style of the button. |
size | "sm" | "md" | "lg" | "icon" | "md" | Sets the height, padding, and font size. |
loading | boolean | false | Shows a spinner and disables interaction. |
leftIcon | ReactNode | — | Icon rendered to the left of the label. |
rightIcon | ReactNode | — | Icon rendered to the right of the label. |
disabled | boolean | false | Prevents all interaction and dims the button. |
Motion
Buttons use spring physics for press feedback — not a linear scale. This preserves energy and feels alive.
| Interaction | Property | Value | Easing |
|---|---|---|---|
| Press | scale | 0.97 | spring(500, 40) |
| Release | scale | 1.0 | spring(500, 40) |
| Hover | opacity / shadow | 0.9 / reduced | 150ms ease |
| Focus | ring | 2px accent | instant |
Mobile
Buttons on touch screens need larger hit areas and thoughtful sizing. Use the viewport toggle above any preview to see how buttons reflow at 390px.
Checkout
Review your order
Pro plan
$12/mo
Extra storage
$2/mo
| Scenario | Guidance |
|---|---|
| Touch target | Minimum 44×44 pt. The md size (h-10 / 40px) is borderline — prefer lg for primary CTAs on mobile. |
| Primary CTA | Use w-full on the primary action at the bottom of a form or screen. Full-width buttons are easier to tap and establish visual hierarchy. |
| Icon-only buttons | Use size="icon" (44px) not a smaller size. Add a tooltip or visible label nearby since hover tooltips don't work on touch. |
| Haptic feedback | On iOS/Android, trigger UIImpactFeedbackGenerator or Vibration.vibrate() on press for tactile confirmation on destructive or primary actions. |
| Button groups | Avoid more than 2 side-by-side buttons on narrow screens. Stack vertically or use a split-button to consolidate. |
ARIA roles
| Element | Role | Key attributes |
|---|---|---|
| Root | button (implicit) | aria-busy, aria-disabled, aria-label |
Keyboard
| Key | Action |
|---|---|
| Enter / Space | Activate the button |
| Tab | Move focus to the next focusable element |
| Shift+Tab | Move focus to the previous focusable element |
Accessibility
- →Uses a native <button> element — keyboard navigable and screen-reader accessible by default.
- →Focus ring is visible only with keyboard navigation (focus-visible).
- →Loading state disables the button and announces via aria-busy="true".
- →Danger variant maintains WCAG AA contrast (4.8:1 on white).
- →Icon-only buttons should include aria-label for assistive technology.