Button
A pressable action trigger with four intents, three sizes, and text or icon-only shapes.
Measured from Attio — brand #266df0, heights 28/32/40px, radii r8/r9/r10.
Preview
Installation
bun add crispimport "crisp/styles.css";
import { Button } from "crisp";Usage
import { Button } from "crisp";
export function Example() {
return (
<Button intent="primary" size="md">
Save
</Button>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
intent | "primary" | "neutral" | "danger" | "ghost" | "primary" | Sets the button's color role and visual weight. |
size | "sm" | "md" | "lg" | "md" | Sets height, padding, and font size — 28 / 32 / 40px. |
shape | "text" | "icon" | "text" | "icon" removes horizontal padding and forces a square, 1:1 aspect ratio. |
loading | boolean | false | Shows a spinner, disables the button, and sets aria-busy. |
asChild | boolean | false | Renders the passed child element instead of a <button>, forwarding classes (polymorphic). |
...props | ComponentPropsWithoutRef<"button"> | — | All native button attributes: onClick, disabled, type, etc. |
Examples
Intent
All four intents — neutral and ghost measured from Attio's toolbar, primary and danger from its primary CTAs.
<Button intent="primary">Save</Button>
<Button intent="neutral">Cancel</Button>
<Button intent="danger">Delete</Button>
<Button intent="ghost">Dismiss</Button>Size
Three heights: 28px (sm), 32px (md), 40px (lg).
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>With icons
Pass an <svg> as a leading or trailing child — the button's gap: 6px handles spacing.
<Button intent="primary">
<PlusIcon />
New task
</Button>
<Button intent="neutral">
Export
<ChevronDownIcon />
</Button>Icon-only
shape="icon" produces a square button. Always pass aria-label since there is no visible text.
<Button intent="neutral" shape="icon" aria-label="Settings">
<GearIcon />
</Button>Loading
loading shows a self-animating spinner, disables the button, and sets aria-busy="true".
<Button intent="primary" loading>
Saving…
</Button>Disabled
Native disabled — 50% opacity, pointer disabled.
<Button intent="primary" disabled>Save</Button>
<Button intent="neutral" disabled>Cancel</Button>asChild
Polymorphic rendering — the button's classes and behavior are forwarded onto the child element instead of a <button>. Here it renders an <a>.
<Button intent="primary" asChild>
<a href="/tasks/new">New task</a>
</Button>Accessibility
Button preserves the browser's default focus-visible outline — crisp does not suppress or override it. When using shape="icon", always provide an accessible name via aria-label or aria-labelledby, since there is no visible text for assistive technology to read. loading sets both disabled and aria-busy="true", so screen readers announce the busy state and duplicate submissions are prevented.
| Key | Action |
|---|---|
Enter / Space | Activates the focused button. |
Tab | Moves focus to the next focusable element. |