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.
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.
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.
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.
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.