Button
Primary interaction element. Built on Base UI ButtonPrimitive with class-variance-authority (CVA) for variant composition. Use sparingly -- max 1 primary button per visible viewport.
Variants
Six visual variants to communicate different levels of emphasis and intent.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "outline" | "ghost" | "destructive" | "link" | "default" | Visual style of the button |
| size | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | "default" | Size of the button |
| disabled | boolean | false | Disables the button (opacity 50%, no pointer events) |
| className | string | --- | Additional CSS classes to merge |
| children | ReactNode | --- | Button content |
States Matrix
Every combination of variant and interactive state. Hover, focus, and active states are force-applied via inline styles and CSS overrides so all states are visible simultaneously.
| Variant | Default | Hover | Focus | Active | Disabled |
|---|---|---|---|---|---|
| default | |||||
| secondary | |||||
| outline | |||||
| ghost | |||||
| destructive | |||||
| link |
Sizes
Four text sizes plus four icon-only sizes for different contexts.
Icon + Label
Compose an icon with visible text by adding data-icon="inline-start" (or "inline-end") to the icon element — it tells the button variant to trim its own padding on that side (px-2.5 → pl-2/pr-2), since the icon plus the gap-1.5 token already carry the visual weight there. Previously defined in buttonVariants but never used anywhere in the app until this example.
Icon Buttons
Use icon sizes for toolbar actions and compact controls. Lucide icons at 16px.

VoiceMock was built by one designer
I'm Sergiy, a product designer. I built this trainer myself — research, design system, code. On LinkedIn I show how products like this come together, and what it changes in a designer's work.
Product Designer · AI Design Engineer · Designer for Startups
See it on LinkedIn