Button
Buttons are the primary interactive elements for triggering actions.
Variants
| Variant | Usage | Example |
|---|
| Primary | Main actions, form submissions | Save, Submit, Create |
| Secondary | Alternative actions | Cancel, Back |
| Outline | Tertiary actions with visible boundary | Edit, View |
| Ghost | Minimal emphasis, toolbar actions | Close, Menu items |
| Destructive | Dangerous/irreversible actions | Delete, Remove |
| Link | Inline text links styled as buttons | Learn more |
Sizes
| Size | Height | Padding | Usage |
|---|
| sm | 32px | py-1 px-3 | Compact UI, tables |
| default | 36px | py-1.5 px-4 | Standard usage |
| lg | 40px | py-2 px-6 | Prominent actions |
| icon | 36px | p-2 | Icon-only buttons |
States
| State | Description |
|---|
| Default | Normal interactive state |
| Hover | Mouse over the button |
| Focus | Keyboard focus with visible ring |
| Active/Pressed | During click |
| Disabled | Non-interactive, reduced opacity |
| Loading | Action in progress with spinner |
Icon Usage
Icons can be placed before or after button text:
<Button>
<PlusIcon className="size-4" />
Create New
</Button>
<Button>
Download
<DownloadIcon className="size-4" />
</Button>
Guidelines
- Icon size should be 16px (
size-4) for default buttons
- Maintain 8px gap between icon and text
- Use descriptive icons that reinforce the action
Accessibility
- Always include accessible label (visible text or
aria-label)
- Disabled buttons should have
disabled attribute
- Loading state should include
aria-busy="true"
- Focus ring must be clearly visible
Do's and Don'ts
Do
- Use Primary for the main action on a page/dialog
- Use Destructive for irreversible actions
- Keep button labels short and action-oriented
- Show loading state for async actions
Don't
- Don't use multiple Primary buttons in the same context
- Don't use Destructive for non-dangerous actions
- Don't disable buttons without explanation
- Don't use icon-only buttons without tooltip
Code Example
import { Button } from "@/components/ui/button"
import { Plus, Trash2, Loader2 } from "lucide-react"
// Primary
<Button>Save Changes</Button>
// Secondary
<Button variant="secondary">Cancel</Button>
// Destructive
<Button variant="destructive">
<Trash2 className="size-4" />
Delete
</Button>
// Loading
<Button disabled>
<Loader2 className="size-4 animate-spin" />
Saving...
</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>