← All foundations
Components · card 1 of 3

Buttons — eight variants, one primary

Transcribed from source_examples/common/Button.vue and source_examples/common/GlowButton.vue. Every control is a 44px pill: padding: 4px 24px, border-radius: 9999px, 16px bold label. Each row below shows rest, hover and active side by side — the hover clones are forced with a class so states are reviewable without a pointer.

Glow — the one true CTA

Lighter violet field #9980FF with #1B0D4E text (8.0:1) and a three-ring violet halo — 0 0 64px #7A58FFA8, 0 0 16px #4F22FFA6, 0 0 4px 2px #5B34FA, verbatim from GlowButton.vue. Three light sources stack: the halo, a blurred highlight travelling under the fill, and an edge bloom sitting 8px outside each end. The fill never changes colour — there is no hover state on the background; the pointer moves the highlight and cross-fades the two edge blooms. At rest the highlight sits at 20% and the left edge burns, which is the state shipped on every page. At most one per action — a long page may repeat it once at the very end, never twice in a viewport.

.btn-glow-field > .btn .btn-glow fill glow-25 · text brand-800 · halo · edge bloom
Discuss project for freerest — hover me Discuss project for freepointer at the right end Discuss project for freeactive · scale(0.98)

The wrapper is not optional. In GlowButton.vue the two .edge-glow fields are siblings of the <a>, not children, because the pill itself is overflow: hidden — nested, they would be clipped at the very edge they are meant to escape.

The remaining variants

.btn .btn-primary solid violet · form submits, in-card confirmations
rest · #5F20FE hover · #4004AF active · #1E113B
.btn .btn-secondary raised surface · the quiet neighbour of a primary
rest · #1E113B hover · #1C1C27
.btn .btn-tertiary bordered · the workhorse, used dozens of times per page
Learn morerest · 1px #5F20FE Learn morehover · border #4004AF
.btn .btn-ghost nav items · no box, bold label
Servicesrest · #F4F4F6 Serviceshover · #B19AFE
.btn .btn-text footer and inline tertiary entry points
Privacy policyrest · #C7C7DB Privacy policyhover · #B19AFE
.btn .btn-link inline link inside running copy
Read the case studyrest · #B19AFE, underlined
.btn .btn-primary:disabled disabled is the ONLY state allowed to reduce contrast
opacity 0.2 · cursor not-allowed

Focus states

Tab through this row. Every focusable control takes a 2px --color-hovered ring at 2px offset — visible against both the page and a card, and never replaced by a color-only cue.

Action economy in practice

The real pairing from source_examples/homepage/testimonial.astro: one glow CTA, one bordered secondary, and the copy does not repeat. This is the only button group shape the system sanctions.

Failure mode. Two glow buttons in one viewport, or three buttons for the same action, is a failed delivery. Secondary entry points are .btn-tertiary, .btn-ghost or .btn-text, and their copy differs from the primary’s.