Skip to content

Gallery / scroll / Scroll-drawn path

Scroll-drawn path

A path draws itself as its container scrolls (scroll-driven animation)

scroll advanced scrolldrawprogressscroll-timeline
Live preview

⚠ Browser support Scroll-driven animations need Chrome/Edge 115+ (Firefox behind a flag); unsupported browsers show the path fully drawn, static.

Techniques: animation-timeline: scroll() · @supports · pathLength · stroke-dashoffset

Customizeupdates preview, code & clipboard
#7F77DD
260px
3px
Code snippets/scroll/scroll-progress-path.html
<!--
  ============================================================
  SVGarden — https://www.svgarden.dev
  Author : Simon-Pierre Boucher
  Contact: contact@spboucher.ai
  File   : snippets/scroll/scroll-progress-path.html
  Desc   : A path draws itself as its container scrolls (scroll-driven animation)
  ============================================================
-->
<div class="sg-scroll-progress-path" style="--sg-color:#7F77DD; --sg-size:260px; --sg-stroke:3px;">
  <div class="sg-scroll-progress-path-track">
    <div class="sg-scroll-progress-path-sticky">
      <svg viewBox="0 0 200 120" aria-hidden="true" focusable="false">
        <path class="sg-scroll-progress-path-ghost" pathLength="100"
          d="M12 90 C 40 20, 60 20, 80 60 C 100 100, 120 100, 140 40 C 150 15, 170 25, 188 45" />
        <path class="sg-scroll-progress-path-line" pathLength="100"
          d="M12 90 C 40 20, 60 20, 80 60 C 100 100, 120 100, 140 40 C 150 15, 170 25, 188 45" />
      </svg>
      <span class="sg-scroll-progress-path-hint">scroll ↓</span>
    </div>
  </div>
</div>
<style>
  .sg-scroll-progress-path {
    width: 100%;
    min-width: 220px;
    height: var(--sg-size);
    overflow-y: scroll;
    border: 1px solid rgba(128, 128, 128, 0.3);
    border-radius: 10px;
  }
  .sg-scroll-progress-path-track {
    height: calc(var(--sg-size) * 3);
  }
  .sg-scroll-progress-path-sticky {
    position: sticky;
    top: 0;
    height: var(--sg-size);
    display: grid;
    place-items: center;
  }
  .sg-scroll-progress-path-sticky svg {
    width: 78%;
    display: block;
  }
  .sg-scroll-progress-path-hint {
    position: absolute;
    right: 12px;
    bottom: 8px;
    font-size: 12px;
    opacity: 0.6;
  }
  .sg-scroll-progress-path-ghost {
    fill: none;
    stroke: rgba(128, 128, 128, 0.25);
    stroke-width: var(--sg-stroke);
    stroke-linecap: round;
  }
  .sg-scroll-progress-path-line {
    fill: none;
    stroke: var(--sg-color);
    stroke-width: var(--sg-stroke);
    stroke-linecap: round;
    stroke-dasharray: 100;
    stroke-dashoffset: 0; /* fallback: fully drawn */
  }
  @supports (animation-timeline: scroll()) {
    .sg-scroll-progress-path-line {
      animation: sg-scroll-progress-path-draw linear both;
      animation-duration: auto;
      animation-timeline: scroll(nearest);
    }
  }
  @keyframes sg-scroll-progress-path-draw {
    from { stroke-dashoffset: 100; }
    to   { stroke-dashoffset: 0; }
  }
</style>

More scroll