Button
The pill button. Label as children, w-fit by default, type=button by default, asChild for links and triggers, one focus halo, one disabled recipe.
Variants
Primary is electric blue (blue-700 hover) — the one action of a form, a modal or an empty state. Secondary is the neutral button for everything else (white fill, hairline). Inverted is the ink CTA, kept for the few public, unauthenticated pages that want it. Pink/dark/sky carry the brand pairings for special moments.
Sizes
Three sizes share one vocabulary across the control family (Button, IconButton, Input, Select). Heights are h-8 / h-10 / h-11 on the 8pt grid; text steps 12 → 14 → 16 on the closed type scale. The app is compact-first: sm is the default — reach for md on prominent/primary CTAs and lg for hero actions.
Sizes with icon
A leading icon scales with the button — size-3.5 / size-4 / size-5 across sm / md / lg so the icon-to-label ratio stays constant.
Outline & ghost
Low-emphasis variants for secondary actions (shadcn outline/ghost, adapted to our tokens). Outline carries a hairline border; ghost is borderless and flat — no hover lift. Each comes in a neutral and a brand-blue tint.
Disabled states
Disabled keeps each variant's hue at reduced strength (per the brand button-states sheet) instead of collapsing to gray.
With icons
Buttons can include a leading icon.
Loading, then done
The spinner fades in over the label, which keeps its space; the button keeps its colour (no disabled dimming). `success` shows a check in the icon slot for the done beat — pair it with a changed label and clear it after a moment (useSaveState).
With keyboard shortcut
Display a keyboard shortcut hint inside the button.
Disabled
Disabled buttons with optional tooltip explaining why.
Width
A button is an inline control and sizes to its label (w-fit). Pass w-full to stretch it — a form's submit button, a card's single CTA.
Long labels
A width-constrained w-full button truncates its label and keeps its icon and shortcut whole; a w-fit button in a tight row keeps its full width instead of clipping.
asChild — links and triggers
Render another element with the button's classes. The child owns its own props; icon and right are placed inside it around its content, while loading and shortcut do not apply.
Icon-only sizes
icon / icon-md / icon-lg are square steps matching sm / md / lg. The SVG is sized to the step unless it carries its own size-* class. Always pass aria-label.
Shortcut and disabled tooltip
shortcut renders a Kbd on md+ screens. disabledTooltip disables the button and explains why on hover; the button stays a real, focusable element.