Skip to main content

Button

πŸŒ“Dark Mode Compatible

An interactive action element with a two-dimensional variant system (appearance x variant) for flexible visual styling and semantic meaning.

Overview​

Use Button when you need:

  • A primary or secondary call-to-action in forms, dialogs, or pages
  • Destructive actions with clear danger styling
  • Success confirmations or informational actions with semantic colors
  • Navigation elements styled as buttons (via polymorphic as="a")
  • Icon-only actions in compact toolbars
  • Loading states with a built-in spinner
Live demoInteractive

Anatomy​

The Button component is a single interactive element with optional icons and loading state:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Button β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Icon β”‚ β”‚ Button Text β”‚ β”‚ Icon β”‚ β”‚
β”‚ β”‚ Left β”‚ β”‚ (children) β”‚ β”‚ Right β”‚ β”‚
β”‚ β”‚ (opt) β”‚ β”‚ β”‚ β”‚ (opt) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ Loading spinner replaces iconLeft when loading β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Parts:

  • Container: single button or a element (controlled by as)
  • Icon Left (optional)
  • Text Content (optional for icon-only buttons)
  • Icon Right (optional)
  • Loading Spinner (replaces icon left when loading is true)

Usage​

import { Button } from '@grasdouble/lufa_design-system';

export function ButtonExamples() {
return (
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
<Button>Click me</Button>
<Button appearance="solid" variant="primary">
Save Changes
</Button>
<Button appearance="outline" variant="secondary">
Cancel
</Button>
<Button appearance="solid" variant="danger" iconLeft="trash">
Delete
</Button>
<Button appearance="solid" variant="primary" loading>
Saving...
</Button>
<Button as="a" href="/home" appearance="ghost" variant="neutral">
Go Home
</Button>
<Button iconLeft="search" aria-label="Search" />
</div>
);
}

Props​

PropTypeDefaultDescription
appearance'solid' | 'outline' | 'ghost''solid'Visual style (filled, border-only, or minimal)
type'button' | 'submit' | 'reset''button'Native HTML button type
variant'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'neutral''primary'Semantic color variant
size'sm' | 'md' | 'lg''md'Button size
radius'none' | 'sm' | 'base' | 'md' | 'full''base'Border radius
iconLeftIconNameundefinedIcon to display on the left side
iconRightIconNameundefinedIcon to display on the right side
loadingbooleanfalseLoading state - shows spinner and disables button
disabledbooleanfalseDisabled state - prevents interaction
fullWidthbooleanfalseMakes button stretch to 100% of container width
as'button' | 'a''button'HTML element to render (polymorphic)
childrenReactNodeundefinedButton text content (optional for icon-only buttons)
classNamestringundefinedAdditional CSS classes

Also supports all standard HTML attributes for the underlying element:

  • When as="button": all button attributes (onClick, name, value, form)
  • When as="a": all a attributes (href, target, rel, download)
  • Common attributes: id, aria-*, data-*, style, tabIndex, role

Accessibility​

  • Provide aria-label for icon-only buttons.
  • Set type="submit" or type="reset" explicitly for form actions; the safe default is button.
  • Use as="a" with href for navigation actions.
  • Loading sets aria-busy and disables interaction.
  • Focus indicators are provided via :focus-visible styles.
import { Button } from '@grasdouble/lufa_design-system';

export function AccessibleButtons() {
return (
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
<Button iconLeft="search" aria-label="Search" />
<Button as="a" href="/dashboard">
Go to Dashboard
</Button>
<Button loading>Saving...</Button>
</div>
);
}

Theming & Tokens​

Button uses design-system tokens for color, spacing, radius, and focus styles. Variants map to semantic color tokens, and sizes map to spacing and typography tokens. Adjust tokens in your theme to change appearance consistently across all variants.

Do / Don’t​

Do
  • Use appearance="solid" with variant="primary" for the most important action on a page.
  • Use appearance="outline" or appearance="ghost" for secondary actions.
  • Use variant="danger" for destructive actions.
  • Provide descriptive labels for all buttons.
  • Use loading during async operations.
Don't
  • Don't use multiple primary solid buttons on the same page.
  • Don't use variant="danger" for non-destructive actions.
  • Don't create icon-only buttons without aria-label.
  • Don't disable buttons without explaining why.
  • Don't use as="a" without href.
  • Icon - Icons used within buttons (via iconLeft, iconRight props)
  • IconButton - Dedicated icon-only button component (if implemented)
  • ButtonGroup - Group of related buttons with unified styling (if implemented)
  • Link - Text link component for inline navigation
  • Box - Layout container for spacing