Skip to content

Gallery / buttons / Neon trace button

Neon trace button

Neon light dash orbiting the button border with a hue sweep

buttons advanced buttonneondasharrayat-property
Live preview

⚠ Browser support @property is required for the smooth hue sweep (Chromium, Safari 16.4+, Firefox 128+); older browsers show the orbiting dash at a fixed hue.

Techniques: @property · stroke-dasharray-gap · hsl-calc · drop-shadow

Customizeupdates preview, code & clipboard
255
16px
2.2s
Code snippets/buttons/btn-neon-trace.html
<!--
  ============================================================
  SVGarden — https://www.svgarden.dev
  Author : Simon-Pierre Boucher
  Contact: contact@spboucher.ai
  File   : snippets/buttons/btn-neon-trace.html
  Desc   : Neon light dash orbiting the button border with a hue sweep
  ============================================================
-->
<button class="sg-btn-neon-trace" type="button" style="--sg-neon-hue:255; --sg-size:16px; --sg-duration:2.2s;">
  <svg viewBox="0 0 100 40" preserveAspectRatio="none" aria-hidden="true" focusable="false">
    <rect class="sg-btn-neon-trace-track" x="1.5" y="1.5" width="97" height="37" rx="9" pathLength="100" />
    <rect class="sg-btn-neon-trace-spark" x="1.5" y="1.5" width="97" height="37" rx="9" pathLength="100" />
  </svg>
  <span class="sg-btn-neon-trace-label">Neon trace</span>
</button>
<style>
  @property --sg-btn-neon-trace-cycle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
  }
  .sg-btn-neon-trace {
    --sg-btn-neon-trace-cycle: 0deg;
    position: relative;
    appearance: none;
    border: 0;
    background: transparent;
    font: inherit;
    font-size: var(--sg-size);
    font-weight: 600;
    color: inherit;
    padding: 0.8em 1.9em;
    cursor: pointer;
    animation: sg-btn-neon-trace-hue calc(var(--sg-duration) * 3) linear infinite;
  }
  .sg-btn-neon-trace svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: visible;
  }
  .sg-btn-neon-trace rect {
    /* no non-scaling-stroke: it makes Chromium measure dashes in screen space,
       desynchronizing the pathLength-based orbit */
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
  }
  .sg-btn-neon-trace-track {
    stroke: rgba(128, 128, 128, 0.35);
  }
  .sg-btn-neon-trace-spark {
    stroke: hsl(calc(var(--sg-neon-hue) * 1deg + var(--sg-btn-neon-trace-cycle)) 90% 62%);
    stroke-dasharray: 14 86;
    stroke-dashoffset: 0;
    filter: drop-shadow(0 0 4px hsl(calc(var(--sg-neon-hue) * 1deg + var(--sg-btn-neon-trace-cycle)) 90% 62%));
    animation: sg-btn-neon-trace-orbit var(--sg-duration) linear infinite;
  }
  .sg-btn-neon-trace:hover .sg-btn-neon-trace-spark,
  .sg-btn-neon-trace:focus-visible .sg-btn-neon-trace-spark {
    stroke-dasharray: 30 70;
  }
  @keyframes sg-btn-neon-trace-orbit {
    to { stroke-dashoffset: -100; }
  }
  @keyframes sg-btn-neon-trace-hue {
    from { --sg-btn-neon-trace-cycle: 0deg; }
    to   { --sg-btn-neon-trace-cycle: 360deg; }
  }
</style>

More buttons