ComponentsActions

Button

A button starts an action: saving, adding, importing, sending. If it takes you somewhere, it's a link.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

import { Button } from '@/ui/button';<Button>  Add job</Button>

Default

Use the default button for most actions on a page. It is the secondary appearance: white, with the control ring.

import { Button } from '@/ui/button';

export default function ButtonDefault() {
  return <Button>Import</Button>;
}

Appearance

Primary

The main action of a page or dialog. Use one per page.

import { Button } from '@/ui/button';

export default function ButtonPrimary() {
  return <Button variant="primary">Add job</Button>;
}

Secondary

Every other action that needs to be seen, such as Import or Cancel. Put secondary actions before the primary.

import { Button } from '@/ui/button';

export default function ButtonSecondary() {
  return (
    <div className="flex flex-wrap gap-100">
      <Button variant="secondary">Cancel</Button>
      <Button variant="primary">Add candidate</Button>
    </div>
  );
}

Ghost

Quiet actions in toolbars, empty states and rows. It has no fill until hover.

import { LinkSimpleIcon, PlusIcon } from '@phosphor-icons/react';
import { Button } from '@/ui/button';

export default function ButtonGhost() {
  return (
    <div className="flex flex-wrap gap-050">
      <Button variant="ghost">
        <LinkSimpleIcon />
        Copy link
      </Button>
      <Button variant="ghost">
        <PlusIcon />
        Add by hand
      </Button>
    </div>
  );
}

Danger

Soft red for the confirm step of something that can't be undone, such as deleting one record. Solid red (danger-bold) is only for a permanent action on many records, after the person types a phrase to confirm.

import { Button } from '@/ui/button';

export default function ButtonDanger() {
  return (
    <div className="flex flex-wrap gap-100">
      <Button variant="danger">Delete company</Button>
      <Button variant="danger-bold">Delete 24 candidates</Button>
    </div>
  );
}

States

Disabled

A disabled button keeps its label and dims to 50%. It stays focusable, so a tooltip can say why it can't be used.

import { Button } from '@/ui/button';

export default function ButtonDisabled() {
  return (
    <div className="flex flex-wrap gap-100">
      <Button disabled>Import</Button>
      <Button variant="primary" disabled>
        Add job
      </Button>
    </div>
  );
}

Selected

Pass selected to make a button a toggle. It sets aria-pressed and the selected fill.

import { useState } from 'react';
import { StarIcon } from '@phosphor-icons/react';
import { Button } from '@/ui/button';

export default function ButtonSelected() {
  const [starred, setStarred] = useState(true);
  return (
    <Button variant="ghost" selected={starred} onClick={() => setStarred(!starred)}>
      <StarIcon />
      Starred
    </Button>
  );
}

Loading

Press the button to see it. The spinner and loadingLabel ("Sending", "Saving") replace the label, the width stays the same and presses are ignored until the work finishes.

import { useState } from 'react';
import { Button } from '@/ui/button';

export default function ButtonLoading() {
  const [loading, setLoading] = useState(false);
  const send = () => {
    setLoading(true);
    setTimeout(() => setLoading(false), 2000);
  };
  return (
    <Button variant="primary" loading={loading} loadingLabel="Sending" onClick={send}>
      Send CVs
    </Button>
  );
}

Spacing

Two sizes: default is 28px tall, sm is 24px. The label is 14px at both.

import { Button } from '@/ui/button';

export default function ButtonSizes() {
  return (
    <div className="flex flex-wrap items-center gap-100">
      <Button>Save</Button>
      <Button size="sm">Save</Button>
    </div>
  );
}

Button with icon

A 16px icon goes before the label, 6px from it.

import { PlusIcon, UploadSimpleIcon } from '@phosphor-icons/react';
import { Button } from '@/ui/button';

export default function ButtonWithIcon() {
  return (
    <div className="flex flex-wrap gap-100">
      <Button>
        <UploadSimpleIcon />
        Import
      </Button>
      <Button variant="primary">
        <PlusIcon />
        Add job
      </Button>
    </div>
  );
}

Icon button

A square button with only an icon, for More, Close and toolbar tools. label is required: it is read out and shown as a tooltip.

import { DotsThreeIcon, PencilSimpleIcon, XIcon } from '@phosphor-icons/react';
import { IconButton } from '@/ui/button';

export default function ButtonIconOnly() {
  return (
    <div className="flex flex-wrap items-center gap-100">
      <IconButton label="More actions">
        <DotsThreeIcon />
      </IconButton>
      <IconButton label="Edit" variant="secondary">
        <PencilSimpleIcon />
      </IconButton>
      <IconButton label="Close" size="sm">
        <XIcon />
      </IconButton>
    </div>
  );
}

On this page