{ CF }

12 CSS Scroll Animations

Animated Data Stats

Counters that ease toward their value, eight scroll-triggered progress bars, and five SVG ring gauges that fill on reveal.

CSS + JS MIT licensed

Animated Data Stats the 4th of 12 designs in the 12 CSS Scroll Animations collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<!-- S1: BIG COUNTERS -->
<section class="section s1">
  <div class="glow s1-glow"></div>
  <div class="counter-grid">
    <div class="c-card">
      <div class="c-num" data-target="4.2" data-suffix="B">0</div>
      <div class="c-lbl">Global Users</div>
      <div class="c-sub">↑ <span>+18%</span> this quarter</div>
    </div>
    <div class="c-card">
      <div class="c-num" data-target="98.7" data-suffix="%">0</div>
      <div class="c-lbl">Uptime SLA</div>
      <div class="c-sub">↑ <span>+0.3pp</span> vs last year</div>
    </div>
    <div class="c-card">
      <div class="c-num" data-target="340" data-suffix="ms">0</div>
      <div class="c-lbl">Avg Response</div>
      <div class="c-sub">↓ <span>-12%</span> latency</div>
    </div>
    <div class="c-card">
      <div class="c-num" data-target="2.1" data-suffix="M">0</div>
      <div class="c-lbl">API Calls / sec</div>
      <div class="c-sub">↑ <span>+31%</span> throughput</div>
    </div>
    <div class="c-card">
      <div class="c-num" data-target="99.4" data-suffix="%">0</div>
      <div class="c-lbl">Data Accuracy</div>
      <div class="c-sub">↑ <span>+0.8pp</span> precision</div>
    </div>
    <div class="c-card">
      <div class="c-num" data-target="7" data-suffix="x">0</div>
      <div class="c-lbl">Faster Ingestion</div>
      <div class="c-sub">↑ vs <span>legacy stack</span></div>
    </div>
  </div>
</section>

<!-- S2: PROGRESS BARS -->
<section class="section s2">
  <div class="glow s2-glow"></div>
  <div class="s2-head">Regional coverage</div>
  <div class="s2-desc">Market penetration by geography · fiscal year 2024</div>
  <div class="bars">
    <div class="bar-row" style="--w:91%;--bc:#00C8FF;--bc2:#00E5D0"><div class="br-top"><span class="br-name">Americas</span><span class="br-val">91%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:87%;--bc:#39FF14;--bc2:#00E5CC"><div class="br-top"><span class="br-name">APAC</span><span class="br-val">87%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:78%;--bc:#A78BFA;--bc2:#C084FC"><div class="br-top"><span class="br-name">Western Europe</span><span class="br-val">78%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:73%;--bc:#60A5FA;--bc2:#34D399"><div class="br-top"><span class="br-name">Eastern Europe</span><span class="br-val">73%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:62%;--bc:#FBBF24;--bc2:#F59E0B"><div class="br-top"><span class="br-name">ANZ</span><span class="br-val">62%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:58%;--bc:#FB7185;--bc2:#F43F5E"><div class="br-top"><span class="br-name">Latam</span><span class="br-val">58%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:44%;--bc:#F472B6;--bc2:#E879F9"><div class="br-top"><span class="br-name">MEA</span><span class="br-val">44%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
    <div class="bar-row" style="--w:31%;--bc:#94A3B8;--bc2:#64748B"><div class="br-top"><span class="br-name">South Asia</span><span class="br-val">31%</span></div><div class="br-track"><div class="br-fill"></div></div></div>
  </div>
</section>

<!-- S3: RING GAUGES -->
<section class="section s3">
  <div class="glow s3-glow"></div>
  <div class="s3-head">Product health</div>
  <div class="s3-desc">Core metrics · rolling 90-day average</div>
  <div class="rings">
    <div class="ring-item">
      <svg width="110" height="110" viewBox="0 0 100 100"><circle class="r-bg" cx="50" cy="50" r="45"/><circle class="r-arc" cx="50" cy="50" r="45" stroke="#39FF14" style="--off:51"/><text class="r-txt" x="50" y="50" fill="#39FF14">82%</text></svg>
      <div class="r-label">Conversion</div>
    </div>
    <div class="ring-item">
      <svg width="110" height="110" viewBox="0 0 100 100"><circle class="r-bg" cx="50" cy="50" r="45"/><circle class="r-arc" cx="50" cy="50" r="45" stroke="#A78BFA" style="--off:88"/><text class="r-txt" x="50" y="50" fill="#A78BFA">69%</text></svg>
      <div class="r-label">Retention</div>
    </div>
    <div class="ring-item">
      <svg width="110" height="110" viewBox="0 0 100 100"><circle class="r-bg" cx="50" cy="50" r="45"/><circle class="r-arc" cx="50" cy="50" r="45" stroke="#00C8FF" style="--off:20"/><text class="r-txt" x="50" y="50" fill="#00C8FF">93%</text></svg>
      <div class="r-label">Satisfaction</div>
    </div>
    <div class="ring-item">
      <svg width="110" height="110" viewBox="0 0 100 100"><circle class="r-bg" cx="50" cy="50" r="45"/><circle class="r-arc" cx="50" cy="50" r="45" stroke="#FBBF24" style="--off:139"/><text class="r-txt" x="50" y="50" fill="#FBBF24">51%</text></svg>
      <div class="r-label">Growth MoM</div>
    </div>
    <div class="ring-item">
      <svg width="110" height="110" viewBox="0 0 100 100"><circle class="r-bg" cx="50" cy="50" r="45"/><circle class="r-arc" cx="50" cy="50" r="45" stroke="#FB7185" style="--off:34"/><text class="r-txt" x="50" y="50" fill="#FB7185">88%</text></svg>
      <div class="r-label">NPS Score</div>
    </div>
  </div>
  <div class="detail-grid">
    <div class="detail-item"><div class="d-num">14.2K</div><div class="d-lbl">Daily Active</div><div class="d-trend" style="color:#39FF14">+9.4%</div></div>
    <div class="detail-item"><div class="d-num">3.8s</div><div class="d-lbl">Session Length</div><div class="d-trend" style="color:#39FF14">+0.3s</div></div>
    <div class="detail-item"><div class="d-num">2.1%</div><div class="d-lbl">Churn Rate</div><div class="d-trend" style="color:#39FF14">−0.4pp</div></div>
    <div class="detail-item"><div class="d-num">$84</div><div class="d-lbl">Avg Rev/User</div><div class="d-trend" style="color:#39FF14">+$12</div></div>
  </div>
</section>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{background:#030508;overflow-x:hidden;color:#fff}

.section{min-height:100vh;padding:8rem 6vw;position:relative;overflow:hidden}

/* glow */
.glow{
  position:absolute;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,var(--gc),transparent 65%);
}

/* S1: COUNTERS */
.s1-glow{
  --gc:rgba(57,255,20,0.045);
  width:800px;height:800px;
  top:50%;left:50%;transform:translate(-50%,-50%);
}
.counter-grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:2rem;
  max-width:900px;margin:0 auto;
}
.c-card{
  border:1px solid rgba(57,255,20,0.12);border-radius:14px;
  padding:2.5rem 2rem;background:rgba(57,255,20,0.015);
  opacity:0;transform:translateY(44px);
  transition:opacity 0.9s cubic-bezier(0.16,1,0.3,1),transform 0.9s cubic-bezier(0.16,1,0.3,1);
}
.c-card.in{opacity:1;transform:translateY(0)}
.c-card:nth-child(2){transition-delay:0.13s}
.c-card:nth-child(3){transition-delay:0.26s}
.c-num{
  font-family:'Space Mono',monospace;font-weight:700;
  font-size:clamp(48px,6vw,80px);
  color:#39FF14;line-height:1;margin-bottom:0.6rem;
}
.c-lbl{
  font-family:'Space Mono',monospace;font-size:11px;
  letter-spacing:0.14em;text-transform:uppercase;
  color:rgba(255,255,255,0.28);
}
.c-sub{
  font-family:'Space Mono',monospace;font-size:11px;
  color:rgba(57,255,20,0.45);margin-top:1rem;
}
.c-sub span{color:#39FF14}

/* S2: BARS */
.s2{background:#040608}
.s2-glow{
  --gc:rgba(0,200,255,0.035);
  width:700px;height:700px;
  top:40%;left:30%;transform:translate(-50%,-50%);
}
.s2-head{
  font-family:'Syne',sans-serif;font-weight:800;
  font-size:clamp(28px,4vw,48px);
  margin-bottom:0.5rem;
  opacity:0;transform:translateX(-30px);
  transition:opacity 0.8s ease,transform 0.8s cubic-bezier(0.16,1,0.3,1);
}
.s2-head.in{opacity:1;transform:translateX(0)}
.s2-desc{
  font-family:'Space Mono',monospace;font-size:12px;
  color:rgba(0,200,255,0.5);margin-bottom:4rem;
  opacity:0;transition:opacity 0.7s ease 0.3s;
}
.s2-desc.in{opacity:1}
.bars{display:flex;flex-direction:column;gap:2rem;max-width:760px}
.bar-row{
  opacity:0;transform:translateX(-28px);
  transition:opacity 0.7s ease,transform 0.7s cubic-bezier(0.16,1,0.3,1);
}
.bar-row.in{opacity:1;transform:translateX(0)}
.bar-row:nth-child(2){transition-delay:0.08s}
.bar-row:nth-child(3){transition-delay:0.16s}
.bar-row:nth-child(4){transition-delay:0.24s}
.bar-row:nth-child(5){transition-delay:0.32s}
.bar-row:nth-child(6){transition-delay:0.40s}
.bar-row:nth-child(7){transition-delay:0.48s}
.bar-row:nth-child(8){transition-delay:0.56s}
.br-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
.br-name{font-family:'Space Mono',monospace;font-size:12px;color:rgba(255,255,255,0.4)}
.br-val{font-family:'Space Mono',monospace;font-size:12px;color:var(--bc)}
.br-track{height:4px;background:rgba(255,255,255,0.05);border-radius:2px;overflow:hidden}
.br-fill{
  height:100%;border-radius:2px;width:0%;
  background:linear-gradient(to right,var(--bc),var(--bc2));
  transition:width 1.4s cubic-bezier(0.16,1,0.3,1) 0.1s;
}
.bar-row.in .br-fill{width:var(--w)}

/* S3: RINGS + DETAILED STATS */
.s3{background:#030407}
.s3-glow{
  --gc:rgba(170,80,255,0.04);
  width:700px;height:700px;
  top:50%;left:60%;transform:translate(-50%,-50%);
}
.s3-head{
  font-family:'Syne',sans-serif;font-weight:800;
  font-size:clamp(28px,4vw,48px);margin-bottom:0.5rem;
  opacity:0;transform:translateX(-30px);
  transition:opacity 0.8s ease,transform 0.8s cubic-bezier(0.16,1,0.3,1);
}
.s3-head.in{opacity:1;transform:translateX(0)}
.s3-desc{
  font-family:'Space Mono',monospace;font-size:12px;
  color:rgba(170,80,255,0.5);margin-bottom:5rem;
  opacity:0;transition:opacity 0.7s ease 0.3s;
}
.s3-desc.in{opacity:1}
.rings{display:flex;gap:3.5rem;flex-wrap:wrap;justify-content:center;margin-bottom:5rem}
.ring-item{
  display:flex;flex-direction:column;align-items:center;gap:1rem;
  opacity:0;transform:scale(0.65);
  transition:opacity 0.9s ease,transform 0.9s cubic-bezier(0.16,1,0.3,1);
}
.ring-item.in{opacity:1;transform:scale(1)}
.ring-item:nth-child(2){transition-delay:0.15s}
.ring-item:nth-child(3){transition-delay:0.30s}
.ring-item:nth-child(4){transition-delay:0.45s}
.ring-item:nth-child(5){transition-delay:0.60s}
.r-bg{fill:none;stroke:rgba(255,255,255,0.06);stroke-width:8}
.r-arc{
  fill:none;stroke-width:8;stroke-linecap:round;
  stroke-dasharray:283;stroke-dashoffset:283;
  transform-box:fill-box;transform-origin:center;
  transform:rotate(-90deg);
  transition:stroke-dashoffset 1.7s cubic-bezier(0.16,1,0.3,1) 0.15s;
}
.ring-item.in .r-arc{stroke-dashoffset:var(--off)}
.r-label{
  font-family:'Space Mono',monospace;font-size:10px;
  color:rgba(255,255,255,0.28);text-transform:uppercase;
  letter-spacing:0.1em;text-align:center;
}
.r-txt{font-family:'Space Mono',monospace;font-size:15px;font-weight:700;text-anchor:middle;dominant-baseline:middle}

/* detail row */
.detail-grid{
  display:grid;grid-template-columns:repeat(4,1fr);gap:1.5rem;
  max-width:800px;margin:0 auto;
}
.detail-item{
  text-align:center;
  opacity:0;transform:translateY(24px);
  transition:opacity 0.7s ease,transform 0.7s cubic-bezier(0.16,1,0.3,1);
}
.detail-item.in{opacity:1;transform:translateY(0)}
.detail-item:nth-child(2){transition-delay:0.1s}
.detail-item:nth-child(3){transition-delay:0.2s}
.detail-item:nth-child(4){transition-delay:0.3s}
.d-num{
  font-family:'Space Mono',monospace;font-size:clamp(22px,2.5vw,34px);
  font-weight:700;color:#fff;margin-bottom:0.4rem;
}
.d-lbl{font-family:'Space Mono',monospace;font-size:10px;color:rgba(255,255,255,0.28);text-transform:uppercase;letter-spacing:0.1em}
.d-trend{font-family:'Space Mono',monospace;font-size:11px;margin-top:0.4rem}
function animCount(el){
  const target=parseFloat(el.dataset.target);
  const suffix=el.dataset.suffix||'';
  const dec=String(target).includes('.');
  const dur=1900,start=performance.now();
  const tick=now=>{
    const p=Math.min((now-start)/dur,1);
    const ease=1-Math.pow(1-p,4);
    el.textContent=(dec?(ease*target).toFixed(1):Math.round(ease*target))+suffix;
    if(p<1)requestAnimationFrame(tick);
  };
  requestAnimationFrame(tick);
}

const io=new IntersectionObserver(entries=>{
  entries.forEach(e=>{
    if(!e.isIntersecting)return;
    e.target.classList.add('in');
    const n=e.target.querySelector('.c-num');
    if(n&&!n.dataset.done){n.dataset.done='1';animCount(n);}
  });
},{threshold:0.2});

document.querySelectorAll('.c-card,.s2-head,.s2-desc,.bar-row,.s3-head,.s3-desc,.ring-item,.detail-item')
  .forEach(el=>io.observe(el));

Search CodeFronts

Loading…