WellSourced

Button

The primary way to commit an action. One primary per screen — everything else is secondary, ghost, or not a button at all.

Sizes#

Usage#

  • One primary per screen. The primary button points at the single most important action. More than one dilutes the signal.
  • Labels are 1–3 words, sentence case. “Buy direct”, “Sign in”, “Save changes”. No emojis. No all-caps.
  • Ghost is not a decorative button. Use it for low-stakes actions inside a composed surface — “Cancel” in a dialog, “Collapse” on a section.
  • Destructive always gets a confirmation step. Destructive-styled buttons never fire immediately; they open a confirm flow.
  • Loading preserves width. The spinner sits inline so the button doesn't jump.

Props#

NameTypeDefaultDescription
variant"primary" | "secondary" | "ghost" | "destructive""primary"Visual weight. Primary for the one main action per screen.
size"sm" | "md" | "lg""md"Height and padding scale.
loadingbooleanfalseShows an inline spinner and disables the button. Preserves width.
disabledbooleanfalseHTML disabled; prevents pointer and keyboard activation.
asChildbooleanfalseWhen true, passes styling to the immediate child (use for wrapping <a> links).
children*ReactNodeButton label. Keep to 1–3 words, sentence case.

Code#

tsx
import { Button } from "@/components/ui/Button";

export function BuyDirect() {
return <Button>Buy direct</Button>;
}

// Variants
<Button variant="secondary">Learn more</Button>
<Button variant="ghost">Cancel</Button>
<Button variant="destructive">Remove</Button>

// States
<Button loading>Saving</Button>
<Button disabled>Out of stock</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>

Accessibility#

  • Focus ring is the brand teal at 2 px offset. Never remove focus-visible styling.
  • loading sets aria-busy so screen readers announce the pending state.
  • Disabled buttons are not focusable and don't forward pointer events. For “not yet allowed” states, consider helper text instead of a disabled button.
  • Labels work without color alone — the text itself communicates the action.