Button
stableserver-safe
Triggers an action. State is carried by a step on the neutral ramp rather than by movement — nothing translates on press, because motion should acknowledge input, not perform.
Variants
One primary per view. Ghost is the only variant that opts out of the border and shadow, because a row of three shadowed buttons is a mess.
<Button variant="primary">Set course</Button>
<Button>Secondary</Button>
<Button variant="danger">Scuttle</Button>
<Button variant="ghost">Cancel</Button>
<Button disabled>Disabled</Button>Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "primary" | "danger" | "ghost" | "default" | Visual weight. Primary marks the one action you want taken. |
| size | "sm" | "md" | "lg" | "md" | Control height. Medium is 40px, matching inputs and selects. |
| block | boolean | false | Fill the container's width. |
| type | "button" | "submit" | "reset" | "button" | Defaults to button, not submit. A button inside a form that silently submits is the most common UI-kit footgun. |
Guidance
Do
- Label with the verb that happens: Publish, Delete, Send. The toast afterwards says Published.
- Keep one primary button per view so the eye knows where to land.
Don't
- Don't use a button for navigation — that's a Link, and it needs to be middle-clickable.
- Don't disable a submit button to express a validation error; say what's wrong instead.
Accessibility
Renders a real <button>, so Enter and Space work with no extra wiring.
Focus shows the signal-coloured ring at 3px — a focus ring is a warning, and the system already has a colour that means that.
Disabled buttons are not focusable. If the reason isn't obvious on screen, explain it in nearby text.