Components

Button

Triggers an action or navigates to a new page. Available in six variants, three sizes, and supports loading and icon states out of the box.

Anatomy

leftIconchildrenrightIcon
The button accepts an optional left and right icon slot, a label, and a loading state. The icon slots accept any Lucide icon at 14px.

Variants

Six variants cover every use case from primary CTAs to in-line link actions.

Sizes

Three sizes — use sm for compact UI, md for standard forms, and lg for marketing CTAs.

Loading state

Pass loading to replace the label with a spinning Loader2 icon and disable the button.

With icons

Use leftIcon or rightIcon for labeled buttons, and size="icon" for icon-only buttons.

Disabled

All variants respect the native disabled attribute, reducing opacity to 50% and removing pointer events.

Props

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost' | 'outline' | 'destructive' | 'link''primary'Visual style of the button.
size'sm' | 'md' | 'lg' | 'icon''md'Controls height, padding, and font size.
loadingbooleanfalseReplaces content with a spinning loader and disables interaction.
leftIconReact.ReactNodeIcon rendered to the left of the label.
rightIconReact.ReactNodeIcon rendered to the right of the label.
disabledbooleanfalseDisables the button and reduces opacity.
classNamestringAdditional Tailwind classes to merge.
...restButtonHTMLAttributesAll native button props are forwarded.

Usage rules

Do

  • Use primary for the single most important action on a page.
  • Use ghost for tertiary actions that should not compete visually.
  • Always include an aria-label on icon-only buttons.
  • Use destructive only for irreversible actions like delete.

Don't

  • Do not place two primary buttons side-by-side — only one primary per view.
  • Do not use link variant as a navigation element inside prose.
  • Do not mix icon-only and labeled buttons in the same button group.
  • Do not use full-width buttons except in mobile or modal footers.

Accessibility

The component renders a native <button> element — keyboard focus, Enter, and Space activation are handled by the browser for free.

All variants include a focus-visible ring using the brand gold ring token — visible on keyboard nav, hidden on click.

Disabled state uses disabled not aria-disabled, so the element is correctly removed from the accessibility tree.

Loading state announces state change via the Loader icon — pair with a visually-hidden live region for critical async actions if needed.