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%
4.0 KB · 117 lines html
Raw Blame History
1<!--2  ============================================================3  SVGarden — https://www.svgarden.dev4  Author : Simon-Pierre Boucher5  Contact: contact@spboucher.ai6  File   : snippets/scroll/scroll-counter-gauge.html7  Desc   : Circular gauge that fills with scroll position8  ============================================================9-->10<!--svgarden11title: Scroll gauge12slug: scroll-counter-gauge13category: scroll14tags: [scroll, gauge, progress, scroll-timeline]15difficulty: advanced16techniques: ["animation-timeline: scroll()", pathLength, scale-property, "@supports"]17how_it_works: >18  Two animations ride the same scroll timeline: the arc's stroke-dashoffset19  runs 100→0 (pathLength="100" makes the circumference exactly 100, so offset20  equals "percent unscrolled"), while the independent scale property breathes21  the dial from 0.85 to 1 — using scale instead of transform means it can't22  collide with the −90° rotation that starts the arc at twelve o'clock.23  Because both declare animation-duration: auto and scroll(nearest), the24  box's own scrollbar is the only clock. Unsupported browsers skip the25  @supports block and simply show the gauge full.26support: "Scroll-driven animations need Chrome/Edge 115+; other browsers show the gauge fully filled, static."27customizable:28  - { var: "--sg-color", label: "Color",      type: color, default: "#7F77DD" }29  - { var: "--sg-size",  label: "Box height", type: range, min: 180, max: 400, default: 260, unit: px }30  - { var: "--sg-thick", label: "Thickness",  type: range, min: 4, max: 14, default: 8, unit: px }31created: 2026-08-1032-->33<div class="sg-scroll-counter-gauge" style="--sg-color:#7F77DD; --sg-size:260px; --sg-thick:8px;">34  <div class="sg-scroll-counter-gauge-track">35    <div class="sg-scroll-counter-gauge-sticky">36      <svg viewBox="0 0 100 100" aria-hidden="true" focusable="false">37        <circle class="sg-scroll-counter-gauge-rail" cx="50" cy="50" r="42" pathLength="100" />38        <circle class="sg-scroll-counter-gauge-arc" cx="50" cy="50" r="42" pathLength="100" />39        <text class="sg-scroll-counter-gauge-label" x="50" y="55">scroll</text>40      </svg>41      <span class="sg-scroll-counter-gauge-hint">scroll ↓</span>42    </div>43  </div>44</div>45<style>46  .sg-scroll-counter-gauge {47    width: 100%;48    min-width: 200px;49    height: var(--sg-size);50    overflow-y: scroll;51    border: 1px solid rgba(128, 128, 128, 0.3);52    border-radius: 10px;53  }54  .sg-scroll-counter-gauge-track {55    height: calc(var(--sg-size) * 3);56  }57  .sg-scroll-counter-gauge-sticky {58    position: sticky;59    top: 0;60    height: var(--sg-size);61    display: grid;62    place-items: center;63  }64  .sg-scroll-counter-gauge-sticky svg {65    width: min(55%, calc(var(--sg-size) * 0.65));66    display: block;67  }68  .sg-scroll-counter-gauge-hint {69    position: absolute;70    right: 12px;71    bottom: 8px;72    font-size: 12px;73    opacity: 0.6;74  }75  .sg-scroll-counter-gauge-rail,76  .sg-scroll-counter-gauge-arc {77    fill: none;78    stroke: var(--sg-color);79    stroke-width: var(--sg-thick);80    stroke-linecap: round;81    transform: rotate(-90deg);82    transform-origin: 50% 50%;83  }84  .sg-scroll-counter-gauge-rail { opacity: 0.15; }85  .sg-scroll-counter-gauge-arc {86    stroke-dasharray: 100;87    stroke-dashoffset: 0; /* fallback: full */88  }89  .sg-scroll-counter-gauge-label {90    font-family: inherit;91    font-size: 13px;92    font-weight: 600;93    fill: var(--sg-color);94    text-anchor: middle;95  }96  @supports (animation-timeline: scroll()) {97    .sg-scroll-counter-gauge-arc {98      animation: sg-scroll-counter-gauge-fill linear both;99      animation-duration: auto;100      animation-timeline: scroll(nearest);101    }102    .sg-scroll-counter-gauge-sticky svg {103      animation: sg-scroll-counter-gauge-breathe linear both;104      animation-duration: auto;105      animation-timeline: scroll(nearest);106    }107  }108  @keyframes sg-scroll-counter-gauge-fill {109    from { stroke-dashoffset: 100; }110    to   { stroke-dashoffset: 0; }111  }112  @keyframes sg-scroll-counter-gauge-breathe {113    from { scale: 0.85; }114    to   { scale: 1; }115  }116</style>117