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.