Button
Buttons trigger actions. Use the brand variant for the one main action on a surface, lower-emphasis variants for everything else.
Examples
Variants
Sizes
With icons
Loading
Disabled
Icon only
Rounded
Overview
Button is the primary interactive element for triggering actions. It supports multiple variants for different emphasis levels, five sizes, icons, loading states, and accessibility features out of the box.
import { Button } from "@rogo-technologies/ui/button";
<Button variant="brand" size="md">
Click me
</Button>;Usage
Variants
Variants encode emphasis, not taste — pick by the action's role on the surface, not by how much attention you'd like it to get. The emphasis ladder, highest to lowest: brand → outline / muted → ghost → text / link. destructive sits outside the ladder: it encodes danger, not importance.
Always write variant explicitly. The default is brand, so a <Button> without a variant silently becomes a primary CTA.
| Variant | Use it for |
|---|---|
brand | The single most important action on a surface — modal confirm, form submit, page CTA. At most one per page, panel, modal, or form. |
outline | Standalone secondary actions — Cancel next to a brand confirm, header and form side actions. The default when unsure. |
muted | Same tier as outline, for busy surfaces where an extra border adds noise — inside cards, dense panels, filter/pill rows. |
ghost | Repeated and contextual actions — toolbars, icon-only buttons, per-row actions in lists/tables, close/dismiss, "more options". |
destructive | Irreversible or dangerous actions only — delete, remove, revoke. Usually the confirm of a confirmation dialog. |
text | Inline actions sitting in a line of text — captions, helper rows, footnotes. |
link | Navigation-like inline actions — underlines on hover. |
<Button variant="brand">Save changes</Button>
<Button variant="outline">Cancel</Button>
<Button variant="muted">Edit</Button>
<Button variant="ghost">More options</Button>
<Button variant="destructive">Delete</Button>
<Button variant="text">Learn more</Button>
<Button variant="link">View documentation</Button>Sizes
| Size | Height | Use case |
|---|---|---|
xxs | 20px | Compact UI, tags, tight spaces |
xs | 24px | Secondary actions in dense layouts |
sm | 28px | Toolbars, inline actions |
md | 32px | Default — most buttons in the interface |
lg | 36px | Primary CTAs, hero sections |
<Button size="xxs">Tag</Button>
<Button size="md">Submit</Button>
<Button size="lg">Get started</Button>With icons
Use prefix for an icon before the label or suffix for one after. Icons automatically scale to match the button size.
import { IconPlus, IconArrowRight } from "@rogo-technologies/ui/icons";
<Button prefix={<IconPlus />}>Add item</Button>
<Button suffix={<IconArrowRight />}>Continue</Button>
<Button prefix={<IconPlus />} suffix={<IconArrowRight />}>
Add and continue
</Button>Loading
When loading is true, a spinner replaces the prefix icon, the button becomes disabled, and aria-busy is set. Width stays stable to prevent layout shift.
const [isLoading, setIsLoading] = useState(false);
async function handleSubmit() {
setIsLoading(true);
await saveData();
setIsLoading(false);
}
<Button loading={isLoading} onClick={handleSubmit}>
Save changes
</Button>;Disabled
<Button disabled={!isFormValid}>Submit</Button>
<Button disabled={isLoading}>Save</Button>Icon only
Always provide an aria-label for accessibility.
| Size | Dimensions |
|---|---|
xxs | 20×20 |
xs | 24×24 |
sm | 28×28 |
md | 32×32 |
lg | 36×36 |
<Button iconOnly aria-label="Open settings">
<IconSettingsGear3 />
</Button>
<Button variant="ghost" iconOnly aria-label="More options">
<IconMoreHorizontal />
</Button>Rounded
Pill-shaped buttons. Works with all sizes and variants.
<Button rounded>Subscribe</Button>
<Button rounded iconOnly aria-label="Add">
<IconPlus />
</Button>Hit area
hitArea renders an invisible ghost element around the button that enlarges its pointer target by 4/8/12px per edge (sm/md/lg) without affecting layout. Useful for small buttons (xxs/xs) in sparse surroundings, where the 24px minimum pointer target isn't met by the button itself. The ghost can overlap adjacent elements, so don't use it in dense clusters. See the Checkbox hit area docs for details on the mechanism.
<Button size="xxs" hitArea="sm">
Tag
</Button>As link
Render as a different element via render — e.g. a Next.js Link for navigation.
import Link from "next/link";
<Button render={<Link href="/dashboard" />}>Go to Dashboard</Button>
<Button render={<a href="https://example.com" target="_blank" />}>
External link
</Button>API
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "brand" | "outline" | "muted" | "ghost" | "destructive" | "text" | "link" | "brand" | Visual style of the button. |
size | "xxs" | "xs" | "sm" | "md" | "lg" | "md" | Size of the button. |
iconOnly | boolean | false | Renders a square button for icon-only use. |
rounded | boolean | false | Applies fully rounded corners (pill shape). |
prefix | ReactNode | — | Element to render before the label. |
suffix | ReactNode | — | Element to render after the label. |
loading | boolean | false | Shows a spinner and disables the button. |
disabled | boolean | false | Disables the button. |
render | ReactElement | — | Renders as a different element (e.g., Link). |
hitArea | "sm" | "md" | "lg" | — | Enlarges the pointer target by 4/8/12px per edge via an invisible ghost element. |
The Button accepts all standard HTML button attributes.
Guidelines
Do
- Always pass
variantexplicitly — the default isbrand, and an accidental primary is the most common variant bug - Use
brandfor the primary action on a page or in a modal — at most one per surface - Use
outlineormutedfor secondary actions alongside a primary button - Use
ghostfor tertiary, repeated, or contextual actions (toolbars, icon buttons, row actions) - Use
destructiveonly for irreversible or dangerous actions - Pair modal footers as
brand(ordestructive) confirm +outlinecancel - Provide
aria-labelfor icon-only buttons - Use
loadingstate for async operations to provide feedback - Keep button labels concise and action-oriented ("Save", "Delete", "Continue")
Don't
- Don't use multiple
brandbuttons in the same context — there should be one clear primary action; two siblings of equal weight are bothoutlineor bothghost - Don't use
destructivefor non-dangerous actions just for visual emphasis - Don't disable buttons without explaining why (consider a tooltip or helper text)
- Don't use icon-only buttons without
aria-label— they're inaccessible to screen readers - Don't mix too many button variants in one area — it creates visual noise
- Don't use
textorlinkvariants for important actions — they lack visual prominence