primitives
Button
Trigger for the primary action on a screen. For navigation use a `<Link>` or the `link` variant; never wrap a route push in a button.
Buttons drive a single discrete action. The variant axis encodes priority and tone: there should be at most one `default`/`brand` per screen, with everything else ranking below it visually. Sizes match component density — `toolbar` and `xs` for compact tool strips, `default` and `lg` for primary forms and CTAs.
Install
Pull this component (and its dependencies) straight into your app via the shadcn CLI:
npx shadcn@latest add https://design.oapps.io/r/button.jsonOr import from the workspace package:
import { Button } from "@8maverik8/design";Examples
Variants
All shipped variants at default size, in priority order.
<Button>Save changes</Button>
<Button variant="secondary">Save draft</Button>
<Button variant="outline">Filter</Button>
<Button variant="ghost">Dismiss</Button>
<Button variant="destructive">Delete project</Button>
<Button variant="link">Learn more</Button>Sizes
Match button height to component density.
<Button size="lg">Continue</Button>
<Button size="default">Continue</Button>
<Button size="sm">Continue</Button>
<Button size="xs">Continue</Button>With icons
Icons sit inside; the button auto-tightens padding via `data-icon=inline-start|end` slots.
<Button>
<Plus data-icon="inline-start" />
Create campaign
</Button>
<Button variant="outline">
Continue
<ArrowRight data-icon="inline-end" />
</Button>
<Button variant="destructive" size="sm">
<Trash2 data-icon="inline-start" />
Delete
</Button>Disabled
Always via the `disabled` prop — never via CSS.
<Button disabled>Save</Button>
<Button variant="destructive" disabled>Delete</Button>Variants
variant
default(default)Primary action. Filled, monochrome (black on light, white on dark). Use for the single main action on a screen.brandSame shape as default but never goes ghost when disabled — keeps a strong CTA presence in heroes / onboarding.secondaryEqual-weight alternative next to a default button (e.g. Save / Cancel pairs).outlineBordered low-emphasis action — good for filter chips, dropdown triggers, secondary toolbar buttons.ghostNo chrome until hover. Use inside menus, table rows, dense toolbars where buttons should disappear when idle.destructiveIrreversible operations: delete, purge, revoke. Tinted destructive surface — never use as a generic 'cancel'.linkInline action that reads as a link. Prefer when the action sits inside a sentence.
size
default(default)32px tall — forms, dialogs, settings.lg40px — heroes, marketing CTAs, onboarding.sm28px — dense lists, inline row actions.xs24px — chips, table-cell triggers.toolbar26px (`--spacing-toolbar`) — diff/list toolbars.icon | icon-sm | icon-xs | icon-lgSquare icon-only buttons; pair with `aria-label`.
Guidelines
- Use exactly one `default` (or `brand`) button per screen — the primary action.Two filled buttons next to each other read as equal priority and the user has to think twice.
- Use `destructive` for reversible actions like 'Cancel' or 'Discard draft'.Destructive tone trains users that the button is dangerous; using it for safe actions teaches them to ignore the warning.
- Pair an icon with `aria-label` whenever the button has no visible text (icon variants).Screen readers announce nothing without it.
- Disable a button via `pointer-events: none` or hidden CSS — always use the `disabled` prop.Custom disabled styling skips focus management and a11y semantics.
- Use `asChild` to render the button as a `<Link>` when the action is navigation.Keeps semantics correct (right-click → open in new tab works) while inheriting button styling.