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>
);
}