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.

PropTypeDefaultDescription
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
disabledbooleanfalseDisables the button (opacity 50%, no pointer events)
classNamestring---Additional CSS classes to merge
childrenReactNode---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.

VariantDefaultHoverFocusActiveDisabled
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.

Sergiy Klymkovych

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