Button

The action and CTA affordance, polymorphic by intent.

The action and CTA affordance. Polymorphic: with href it is an<a> (a CTA that navigates), otherwise a<button> (a real action) — same look, picked by intent rather than by a prop.

primary fills with --surface-action, which is the brand green in both modes (ADR 0014). secondary is an outline that reads --text-default, so it adapts inside tone contexts where a fill would not. A link-styled affordance stays the Link primitive's job — there is deliberately no third variant.

Hover on primary turns the fill to the dark of its resting text, with a white label, crossfaded over--duration-fast. Pressing deepens the fill toward black. That is the live site's behaviour, not its hexes (ADR 0034, amended 2026-09-30). The fill is dark in both modes, so white beats the resting contrast in both.

Re-tone the preview and toggle the theme: the green fill is held across both modes and every tone, because --surface-action is not tone-aware on purpose (ADR 0013) — a CTA should read as the brand green wherever it lands.

Variants

Tone

Disabled

Only the <button> form has this state: a disabled link is a link you don't render. It drops out of hover and pressed entirely rather than showing a greyed response, which would read as a button that responded.

Tone

Props

packages/primitives/src/Button.astro
PropTypeRequired
variant"primary" | "secondary"—
hrefstring—
type"button" | "submit" | "reset"—
classstring—