In context
A realistic showcase — page-header CTAs, form actions, semantic tonal overrides, and a hard-edge toolbar with the loading state.
Payroll runs
March 2026 cycle — 142 employees
Page-header pattern: one primary action, a secondary outline, a ghost background action, and icon-only chrome on the right.
Invite teammate
Dialog-footer pattern — cancel (ghost) + submit (primary).
Default
import { Button, IconButton } from '@mihcm/ui/Button';
<Button>Save changes</Button>Destructive confirmation
For actions that destroy data — pair with a confirm dialog.
<Button variant="destructive" onClick={handleDelete}>
Delete account
</Button>With leading icon
import { Button } from '@mihcm/ui/Button';
import { Plus } from '@mihcm/icons';
<Button leadingIcon={<Plus className="size-4" />}>
New project
</Button>With icon options
Use leadingIcon and trailingIcon for labelled actions. Use size="icon" only when the icon is the whole affordance and provide an accessible label.
import { ArrowRight, Plus, Search } from '@mihcm/icons';
<Button leadingIcon={<Plus className="size-4" />}>Create</Button>
<Button variant="outline" trailingIcon={<ArrowRight className="size-4" />}>
Continue
</Button>
<IconButton aria-label="Search">
<Search className="size-4" />
</IconButton>Icon only
Use IconButton for icon-only actions. It always renders size="icon" and requires either aria-label or accessibilityLabel.
import { IconButton } from '@mihcm/ui/Button';
import { Search, Settings } from '@mihcm/icons';
<IconButton aria-label="Search">
<Search />
</IconButton>
<IconButton shape="hard" variant="ghost" aria-label="Settings">
<Settings />
</IconButton>Loading state
isLoading sets aria-busy and disables interaction without changing the layout.
<Button isLoading>Saving…</Button>Form button types
Button defaults to type="button" so toolbar and dialog actions do not accidentally submit nearby forms. Set type="submit" only for the submit action.
<Button type="submit">Save payroll run</Button>
<Button type="reset" variant="outline">Reset form</Button>
<Button type="button" variant="ghost">Open help</Button>Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="xl">XL</Button>
<Button size="xxl">XXL</Button>Hard edge
Use shape="hard" for dense enterprise toolbars, grid controls, and edge-aligned page actions where radius should be removed.
<Button shape="hard">Hard primary</Button>
<Button shape="hard" variant="outline">Hard outline</Button>Tailwind class overrides
Button variants are the baseline, not the limit. Use className for semantic Tailwind token utilities when a workflow needs success, warning, destructive, or other system tones. Classes are merged after the variant with tailwind-merge, so border-success replaces the outline variant's default border class.
Avoid raw hex values, arbitrary bracket utilities, inline styles, or local CSS modules. Keep overrides in the MiHCM token/Tailwind surface so light mode, dark mode, focus states, and MCP enforcement stay consistent.
<Button
variant="outline"
className="border-success text-success hover:bg-success/10 focus-visible:ring-success"
>
Approve
</Button>
<Button
variant="outline"
className="border-warning text-warning hover:bg-warning/10 focus-visible:ring-warning"
>
Review
</Button>
<Button
variant="outline"
className="border-destructive text-destructive hover:bg-destructive/10 focus-visible:ring-destructive"
>
Escalate
</Button>All variants together
<div className="flex flex-wrap gap-2">
<Button variant="default">Default</Button>
<Button variant="accent">Accent</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>