Button
Sourcecomponents/Button.astro
The real component — hover, press, tab to it.
Overview
3 variants × 6 states, on light, dark & image — the whole button system.
On light
On dark
On image
Variations
Ways to compose a button beyond the base variants — an icon, a form submit, or full-width. Each works with any variant.
Icon before the label — for actions the icon represents (Save, Download, Add). Slot the SVG first.
Icon after the label — to signal direction. A forward arrow on a “continue” or “start” CTA.
With no href, Button renders a real <button>. Use type="submit" for the button inside a form.
Stretches to its container. Used for stacked CTAs on mobile and inside narrow cards (e.g. pricing tiers) where a button spans the full width.
Anatomy
The button’s internal spacing and text style.
- Text —
--fs-button· 17px · Medium 500 - Padding — 14px vertical ·
--space-lg(24px) horizontal - Icon ↔ label gap —
--space-sm(12px) - Radius —
--radius(20px)
Grouping two buttons? See Button Group for the spacing.
Responsive
Default keeps its width; fullWidth stretches — the mobile CTA pattern. Switch device for real breakpoints; 1920 shows at 100% and scrolls.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'tertiary' | 'primary' | Primary = filled violet · Secondary = filled · Tertiary = outlined. |
surface | 'light' | 'dark' | 'light' | 'dark' = the on-image treatment (Secondary→white fill, Tertiary→white outline). |
href | string | — | If set (and not disabled), renders an <a>; otherwise a <button>. |
type | 'button' | 'submit' | 'button' | Ignored when it renders a link. |
fullWidth | boolean | false | Stretch to 100% — used for stacked mobile CTAs. |
disabled | boolean | false | Dims to 40% and blocks interaction; sets aria-disabled. |
loading | boolean | false | Shows a spinner (label hidden), blocks interaction, sets aria-busy. |
Usage
Import the shared component and set props — never re-style a button by hand.
<Button variant="primary" href="/path">Label</Button>
<Button variant="secondary">Label</Button>
<Button variant="tertiary" surface="dark">Label</Button> Usage rules
Two kinds — how to place buttons in a design, and how to build them in code.
Visual — placement
Use one Primary per view — the single most important action. Pair it with a Secondary or Tertiary for the lesser action.
Switch to the surface="dark" treatment on imagery / dark sections so the button stays legible.
Place two Primaries side by side, or Primary next to Secondary as equals — it removes the hierarchy. Downgrade one.
Mix icon-left and icon-right in the same button, or add an icon that doesn’t clarify the action.
Code — implementation
Import the shared Button; use variant + surface; slot an SVG for an icon.
Pass href for navigation (renders <a>) and type="submit" for forms (renders <button>).
Hand-roll a styled link. Writing background: var(--fg-accent) + border-radius? Import Button instead.
Override the size, radius or font — they come from tokens (--fs-button, --radius).