Button
Displays a button or a component that looks like a button.
Variants
The in-app set. default is the violet action color everywhere; muted
is the neutral filled workhorse on a plain background (the in-app
Save/Cancel look); ghost has no fill at rest — the borderless icon and
row affordance (theme toggle, menu triggers). Two violet tints: soft is
the faint chip (violet-50 — the bookmark tile, "+5 more min"), surface
the stronger surface-brand tint (violet-200) for a tinted button that
must still read as a button beside a primary one (the review mode-toggle
icon buttons).
import { Button } from '@immit/ui/button'
export function ButtonVariants() {
return (
<div className="flex flex-wrap items-center gap-2">
<Button>Default</Button>
<Button variant="muted">Muted</Button>
<Button variant="soft">Soft</Button>
<Button variant="surface">Surface</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="success">Success</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>
)
}
Marketing pills
Marketing CTAs opt into the pill shape per button with
className="rounded-full" and the xl/2xl sizes. inverse is the ink
CTA that flips with the theme; secondary is the white/ink pill for
tinted surfaces — on a plain background it would vanish, which is why
this preview sits on the warm band.
import { Button } from '@immit/ui/button'
export function MarketingButtons() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button size="xl" className="rounded-full">
Add to Chrome
</Button>
<Button variant="inverse" size="xl" className="rounded-full">
Download
</Button>
<Button variant="secondary" size="xl" className="rounded-full">
Log in
</Button>
<Button size="2xl" className="rounded-full">
Get started
</Button>
</div>
)
}
Sizes
import { Button } from '@immit/ui/button'
export function ButtonSizes() {
return (
<div className="flex flex-wrap items-center gap-2">
<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="xl">Extra large</Button>
<Button size="2xl">2XL</Button>
</div>
)
}
Icon Buttons
import { Button } from '@immit/ui/button'
import { FileJson } from 'lucide-react'
export function IconButtons() {
return (
<div className="flex flex-wrap items-center gap-2">
<Button variant="outline" size="icon-xs" aria-label="Extra small">
<FileJson />
</Button>
<Button variant="outline" size="icon-sm" aria-label="Small">
<FileJson />
</Button>
<Button variant="outline" size="icon" aria-label="Default">
<FileJson />
</Button>
<Button variant="outline" size="icon-lg" aria-label="Large">
<FileJson />
</Button>
<Button variant="outline" size="icon-xl" aria-label="Extra large">
<FileJson />
</Button>
</div>
)
}
Loading
import { Button } from '@immit/ui/button'
import { Spinner } from '@immit/ui/spinner'
export function ButtonLoading() {
return (
<div className="flex flex-wrap items-center gap-2">
<Button disabled>
<Spinner data-icon="inline-start" />
Saving
</Button>
<Button variant="secondary" disabled>
<Spinner data-icon="inline-start" />
Saving
</Button>
<Button variant="outline" disabled>
<Spinner data-icon="inline-start" />
Saving
</Button>
</div>
)
}
Disabled
import { Button } from '@immit/ui/button'
export function ButtonDisabled() {
return (
<div className="flex flex-wrap gap-2">
<Button disabled>Default</Button>
<Button disabled variant="secondary">
Secondary
</Button>
<Button disabled variant="soft">
Soft
</Button>
<Button disabled variant="destructive">
Destructive
</Button>
<Button disabled variant="success">
Success
</Button>
<Button disabled variant="outline">
Outline
</Button>
<Button disabled variant="ghost">
Ghost
</Button>
<Button disabled variant="link">
Link
</Button>
</div>
)
}