components
Button
Every variant does one job. Pick by intent — the main action on a page is always `default`; destructive operations are always `destructive`. Never recolour a button inline.
Read the full specVariants
Seven variants — each one is a semantic choice.
import { Button } from "@wolf/design-system";
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="success">Success</Button>Sizes
Five sizes. `default` is the safe choice; `xs`/`sm` for dense UIs, `lg` for marketing surfaces, `icon` for toolbar use.
<Button size="xs">xs</Button>
<Button size="sm">sm</Button>
<Button>default</Button>
<Button size="lg">lg</Button>
<Button size="icon" aria-label="Add"><Plus /></Button>Variant × size matrix
Every intended pairing, rendered together so drift is impossible to miss.
With leading and trailing icons
Icons are Lucide, stroke 2, size-4 by default. The button's CSS sizes them automatically.
<Button>
<Download /> Download
</Button>
<Button variant="outline">
Read more <ArrowRight />
</Button>Loading and disabled
Spinner replaces the icon slot; label stays for screen readers. Disabled buttons retain layout but become unreachable.
<Button disabled>
<Loader2 className="animate-spin" /> Submitting…
</Button>asChild — compose with Next <Link>
Set `asChild` when the button should be a link. Radix Slot forwards the button styling onto the child element — the native `<a>` retains SEO and middle-click behaviour.
<Button asChild>
<Link href="/pricing">See pricing <ArrowRight /></Link>
</Button>