Skip to content
OpenPerf
Esc
↑↓navigate↵open⌘Jpreview
On this page

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 primary per view, for the main action on screen.
  • No outlined buttons. Use ghost when a fill is too loud.
  • danger only confirms a destructive action, such as Delete in its confirmation dialog. The button that opens that dialog stays default or ghost.
  • 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

PropType
variant?"default" | "field" | "primary" | "ghost" | "danger"
Type"default" | "field" | "primary" | "ghost" | "danger"
Default"default"
size?"inline" | "xs" | "sm" | "md"
Type"inline" | "xs" | "sm" | "md"
Default"md"
shape?"rect" | "pill" | "square"
Type"rect" | "pill" | "square"
Default"rect"
loading?boolean

Shows a spinner and blocks clicks.

Typeboolean
onClick?(event) => unknown

A returned promise shows the spinner until it settles.

Type(event) => unknown
render?ReactElement | function

Renders another element, such as a router link, with the button's styles.

TypeReactElement | function

Other props go to the native button. Built on Base UI Button.

Was this page helpful?