Button
Runs an action. Five variants, four sizes and three shapes.
import { Button } from "@openperf/deft";
import { Preview } from "../../src/Preview";
export default function ButtonVariants() {
return (
<Preview>
<Button.Root>Default</Button.Root>
<Button.Root variant="field">Field</Button.Root>
<Button.Root variant="primary">Primary</Button.Root>
<Button.Root variant="ghost">Ghost</Button.Root>
<Button.Root variant="danger">Danger</Button.Root>
</Preview>
);
}
- One
primaryper view, for the main action on screen. - No outlined buttons. Use
ghostwhen a fill is too loud. dangeronly confirms a destructive action, such as Delete in its confirmation dialog. The button that opens that dialog staysdefaultorghost.- Labels are verbs in sentence case: “Resolve”, “Create issue”.
- Icons sit before the label; a chevron that opens a menu sits after it.
| Variant | Use |
|---|---|
default |
Header actions: filters, period |
field |
Actions inside a List |
primary |
The one main action on screen |
ghost |
Toolbar, row and menu actions |
danger |
Confirms a destructive action |
Sizes
import { Button } from "@openperf/deft";
import { Preview } from "../../src/Preview";
export default function ButtonSizes() {
return (
<Preview>
<Button.Root>Medium</Button.Root>
<Button.Root size="sm">Small</Button.Root>
<Button.Root size="xs">Extra small</Button.Root>
<p>
Text with an <Button.Root size="inline">inline</Button.Root> button.
</p>
</Preview>
);
}
md is the default, for panels and forms. sm goes in toolbars and the action bar, xs for compact buttons inside a field or a code frame. inline has no fixed height: it takes the font size and line height of the text around it, for a control inside a sentence.
Shapes
import { Button } from "@openperf/deft";
import { IconPlus } from "@tabler/icons-react";
import { Preview } from "../../src/Preview";
export default function ButtonShapes() {
return (
<Preview>
<Button.Root>Rect</Button.Root>
<Button.Root shape="pill">Pill</Button.Root>
<Button.Root aria-label="Add" shape="square">
<IconPlus />
</Button.Root>
</Preview>
);
}
square is for icon-only buttons and needs an aria-label. pill is for buttons on a pill surface, such as the action bar.
Loading
import { Button } from "@openperf/deft";
import { Preview } from "../../src/Preview";
export default function ButtonLoading() {
return (
<Preview>
<Button.Root loading={true}>Loading</Button.Root>
<Button.Root
onClick={() =>
new Promise((resolve) => {
setTimeout(resolve, 1500);
})
}
>
Click to load
</Button.Root>
<Button.Root disabled={true}>Disabled</Button.Root>
</Preview>
);
}
An onClick that returns a promise shows the spinner until it settles. loading shows it on demand. While loading, the button is disabled and sets aria-busy.
API
variant?"default" | "field" | "primary" | "ghost" | "danger"
"default" | "field" | "primary" | "ghost" | "danger""default"size?"inline" | "xs" | "sm" | "md"
"inline" | "xs" | "sm" | "md""md"shape?"rect" | "pill" | "square"
"rect" | "pill" | "square""rect"loading?boolean
Shows a spinner and blocks clicks.
booleanonClick?(event) => unknown
A returned promise shows the spinner until it settles.
(event) => unknownrender?ReactElement | function
Renders another element, such as a router link, with the button's styles.
ReactElement | functionOther props go to the native button. Built on Base UI Button.