Border Beam Card
A matte pricing card with one short amber beam tracing its border at a constant speed. Quiet until you look at it.
Live demo
Hover the card to brighten the beam. It is four stacked SVG dashes on one path, so the head always leads the tail and the speed never changes at the corners.
The Prompt
Build a pricing card with a border beam: a single short beam of warm light that travels around the card's border at a constant speed. Match my project's existing stack, conventions, and design tokens. If there are none, output a single HTML file with inline CSS and JavaScript and zero dependencies.
The goal: an instrument-panel feel. Matte graphite, hairline borders, tabular numerals, one amber beam. Quiet until you look at it, then you can't stop looking.
LOOK
- Page: background #0B0B0C with one soft light pool, radial-gradient(800px 520px at 50% 28%, rgba(255, 181, 71, 0.06), transparent 70%). Center the card both ways with 40px minimum padding.
- Card: width min(380px, 100%), padding 28px, border-radius 20px, background linear-gradient(180deg, #151517 0%, #111113 100%), border 1px solid rgba(255, 255, 255, 0.08), box-shadow inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 30px 80px -20px rgba(0, 0, 0, 0.6).
- Font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif. All numbers use font-variant-numeric: tabular-nums.
- Colors: primary text #F4F2EE, body #D9D6D0, muted #8C8A86, faint #6E6C68, amber #FFB547, deep amber #FF7A1A, beam head #FFE1A8.
- Content, top to bottom:
1. Header row, space-between: plan name "Pro" (15px, weight 600, #F4F2EE) and a pill "Most popular" (11px, weight 600, uppercase, letter-spacing 0.08em, color #FFB547, background rgba(255, 181, 71, 0.10), 1px border rgba(255, 181, 71, 0.25), radius 999px, padding 4px 9px).
2. "For teams shipping every day." 14px, #8C8A86, 6px below the header.
3. Price, 28px below: "$24" at 56px, weight 600, letter-spacing -0.04em, line-height 1, #F4F2EE, followed on the same baseline by "/ month" at 15px, #8C8A86, 6px gap.
4. "Billed yearly · $288" 13px, #6E6C68, 8px below the price.
5. A 1px rgba(255, 255, 255, 0.07) divider with 24px above and below.
6. Feature list, 14px, #D9D6D0, 14px row gap, each row a 16px amber check icon (circle outline plus check, stroke #FFB547, stroke-width 1.75) and the text, 12px gap:
Unlimited projects and seats / Preview deploys on every branch / 90-day analytics retention / Custom domains with auto TLS / Priority support, 4-hour response
7. CTA, 28px below: full-width link styled as a button, height 46px, radius 12px, background #F4F2EE, text #111113, 15px weight 600, label "Start 14-day trial" plus a 16px right arrow icon, 8px gap, centered.
8. "No card required. Cancel anytime." 12px, #6E6C68, centered, 12px below the button.
THE BEAM (constant speed, not a rotating conic gradient)
- An absolutely positioned inline <svg> covering the card's border box (inset -1px), overflow visible, pointer-events none, aria-hidden="true". No viewBox, so 1 user unit equals 1 CSS px.
- Inside it, four <path> elements that all trace the same rounded rectangle: x 0.5, y 0.5, width = cardWidth - 1, height = cardHeight - 1, corner radius 19.5. Build the d attribute in JavaScript, starting at (x + r, y) and running clockwise: H, arc, V, arc, H, arc, V, arc, Z. Recompute it with a ResizeObserver on the card (fallback: window resize). Use <path> rather than <rect>, because pathLength is reliable on paths in every browser. Every path has fill none and pathLength="100", so dash values are percentages of the perimeter.
- Layers, back to front:
- Bloom: stroke #FFB547, stroke-width 6, opacity 0.55, stroke-dasharray "10 90", filter: an SVG feGaussianBlur stdDeviation 4 defined in the same SVG.
- Tail: stroke #FF7A1A, stroke-width 1, opacity 0.35, stroke-dasharray "22 78".
- Near tail: stroke #FF9A2E, stroke-width 1.25, opacity 0.7, stroke-dasharray "12 88".
- Head: stroke #FFE1A8, stroke-width 1.5, stroke-linecap round, stroke-dasharray "4 96".
- Animate stroke-dashoffset with CSS keyframes, 6s linear infinite, all four layers locked together so the head always leads. Each layer moves from a start offset to start - 100:
- Tail: 0 to -100. Near tail: -10 to -110. Bloom: -12 to -112. Head: -18 to -118.
Decreasing offsets move the dashes clockwise from the top-left corner. Use unitless offsets so they stay relative to pathLength.
- The beam must never pause, stutter, or speed up around corners.
HOVER
- Card border goes to rgba(255, 255, 255, 0.14) over 200ms. The beam's svg opacity goes from 0.85 to 1. The beam speed never changes.
- CTA background goes to #FFFFFF and its arrow moves 2px right, 150ms ease.
STRUCTURE
<main> centering one <article class="plan" aria-labelledby="plan-name">, with the beam svg as its first child and the content after it (content position relative, z-index 1). Use a <ul> for the features.
ACCESSIBILITY
- Plan name is an h2 with id plan-name. The price reads as one phrase: put visually hidden text "$24 per month" inside the price element and mark the visible "$24" and "/ month" spans aria-hidden="true".
- The pill and check icons are decorative (aria-hidden="true" on the icons).
- CTA focus ring: 2px solid #FFB547, offset 3px.
- prefers-reduced-motion: no animation. Freeze every layer at its start offset minus 8 (tail -8, near tail -18, bloom -20, head -26), so a still beam rests just past the top-right corner.
NEVER
- A conic-gradient border, a rotating pseudo-element, or a beam that changes speed.
- Purple, cyan, or rainbow beams. Two beams. A beam that pulses.
- Glassmorphism, a card that lifts or scales on hover, or a glow around the whole card.
- Centered body text, emoji, or a second accent color.
- CopyThe full prompt, one click.
- PasteInto Cursor, Claude Code, or any agent.
- CompareAgainst the live demo above.
What’s Inside
Constant-Speed Path
The beam rides a rounded-rectangle path with pathLength="100", so dash values are percentages of the perimeter and speed is even everywhere.
Four-Layer Beam
Bloom, tail, near tail, and head are four dashes that end at the same point. Stacking them fakes a gradient along the stroke.
Locked Offsets
Every layer animates its own dash offset by exactly 100 over the same 6 seconds, so the head never drifts off the tail.
Measured Geometry
A ResizeObserver rebuilds the path whenever the card changes size. The beam stays on the 1px border at any width.
Bloom Filter
A blurred 6px dash behind the beam adds the glow, scoped to the beam instead of the whole card.
Tabular Pricing
Tabular numerals, a baseline-aligned period, and a screen-reader price that reads as one phrase.
Single Accent
Amber appears only in the beam, the pill, and the checks. Everything else is graphite and paper white.
Reduced Motion
The beam freezes just past the top-right corner, so the card still reads as highlighted.
Usage Guidelines
Use This When
- One plan, card, or panel needs to stand out from its siblings
- You want motion that draws the eye without moving the layout
- The interface is dark, technical, and restrained
Not Ideal When
- Several cards on one screen would each get a beam
- The card holds content people read for a long time, like long text or forms
- The design system is light and soft; the beam needs a dark surface