SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%
3.9 KB · 98 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/buttons/btn-neon-trace.html7  Desc   : Neon light dash orbiting the button border with a hue sweep8  ============================================================9-->10<!--svgarden11title: Neon trace button12slug: btn-neon-trace13category: buttons14tags: [button, neon, dasharray, at-property]15difficulty: advanced16techniques: ["@property", stroke-dasharray-gap, hsl-calc, drop-shadow]17how_it_works: >18  A short dash (stroke-dasharray "14 86" on a pathLength-100 rect) endlessly19  slides its dashoffset, so a single bright segment orbits the border. Its color20  is hsl(calc(var(--sg-neon-hue) * 1deg + var(--sg-btn-neon-trace-cycle)) …),21  where the cycle variable is registered with @property as a typed angle — that22  registration is what lets the keyframe animation interpolate it smoothly23  instead of snapping. Browsers without @property still animate the custom24  property, but discretely: the cycle runs 0deg→360deg, and since those are the25  same hue the snap is literally invisible — a degradation path chosen so the26  fallback needs no @supports at all.27support: "@property is required for the smooth hue sweep (Chromium, Safari 16.4+, Firefox 128+); older browsers show the orbiting dash at a fixed hue."28customizable:29  - { var: "--sg-neon-hue", label: "Base hue",  type: range, min: 0, max: 360, default: 255 }30  - { var: "--sg-size",     label: "Font size", type: range, min: 12, max: 26, default: 16, unit: px }31  - { var: "--sg-duration", label: "Orbit time", type: range, min: 0.8, max: 6, step: 0.1, default: 2.2, unit: s }32created: 2026-08-1033-->34<button class="sg-btn-neon-trace" type="button" style="--sg-neon-hue:255; --sg-size:16px; --sg-duration:2.2s;">35  <svg viewBox="0 0 100 40" preserveAspectRatio="none" aria-hidden="true" focusable="false">36    <rect class="sg-btn-neon-trace-track" x="1.5" y="1.5" width="97" height="37" rx="9" pathLength="100" />37    <rect class="sg-btn-neon-trace-spark" x="1.5" y="1.5" width="97" height="37" rx="9" pathLength="100" />38  </svg>39  <span class="sg-btn-neon-trace-label">Neon trace</span>40</button>41<style>42  @property --sg-btn-neon-trace-cycle {43    syntax: '<angle>';44    inherits: false;45    initial-value: 0deg;46  }47  .sg-btn-neon-trace {48    --sg-btn-neon-trace-cycle: 0deg;49    position: relative;50    appearance: none;51    border: 0;52    background: transparent;53    font: inherit;54    font-size: var(--sg-size);55    font-weight: 600;56    color: inherit;57    padding: 0.8em 1.9em;58    cursor: pointer;59    animation: sg-btn-neon-trace-hue calc(var(--sg-duration) * 3) linear infinite;60  }61  .sg-btn-neon-trace svg {62    position: absolute;63    inset: 0;64    width: 100%;65    height: 100%;66    pointer-events: none;67    overflow: visible;68  }69  .sg-btn-neon-trace rect {70    /* no non-scaling-stroke: it makes Chromium measure dashes in screen space,71       desynchronizing the pathLength-based orbit */72    fill: none;73    stroke-width: 2;74    stroke-linecap: round;75  }76  .sg-btn-neon-trace-track {77    stroke: rgba(128, 128, 128, 0.35);78  }79  .sg-btn-neon-trace-spark {80    stroke: hsl(calc(var(--sg-neon-hue) * 1deg + var(--sg-btn-neon-trace-cycle)) 90% 62%);81    stroke-dasharray: 14 86;82    stroke-dashoffset: 0;83    filter: drop-shadow(0 0 4px hsl(calc(var(--sg-neon-hue) * 1deg + var(--sg-btn-neon-trace-cycle)) 90% 62%));84    animation: sg-btn-neon-trace-orbit var(--sg-duration) linear infinite;85  }86  .sg-btn-neon-trace:hover .sg-btn-neon-trace-spark,87  .sg-btn-neon-trace:focus-visible .sg-btn-neon-trace-spark {88    stroke-dasharray: 30 70;89  }90  @keyframes sg-btn-neon-trace-orbit {91    to { stroke-dashoffset: -100; }92  }93  @keyframes sg-btn-neon-trace-hue {94    from { --sg-btn-neon-trace-cycle: 0deg; }95    to   { --sg-btn-neon-trace-cycle: 360deg; }96  }97</style>98