Back to CSS Number Counter Animations Fundraising Arc Meter CSS + JS
Share
HTML
<div class="cnc-fund">
  <div class="cnc-fund-card">
    <div class="cnc-fund-eyebrow">Reforestation Fund · 2026</div>
    <div class="cnc-fund-arc-wrap">
      <svg width="240" height="130" viewBox="0 0 240 130">
        <defs>
          <linearGradient id="cnc-fund-arcGrad" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0%" stop-color="#7a3f0a"/>
            <stop offset="100%" stop-color="#ffb347"/>
          </linearGradient>
        </defs>
        <path class="cnc-fund-arc-bg" d="M 20,130 A 100,100 0 0,1 220,130"/>
        <path class="cnc-fund-arc-fill" d="M 20,130 A 100,100 0 0,1 220,130"/>
      </svg>
      <div class="cnc-fund-arc-center">
        <div class="cnc-fund-big-num"><sup>$</sup><span class="cnc-num" data-target="390" data-format="round">0</span></div>
      </div>
    </div>
    <div class="cnc-fund-pct-label"><span class="cnc-num" data-target="78" data-format="round">0</span>% of goal reached</div>
    <div class="cnc-fund-divider"></div>
    <div class="cnc-fund-goal-row">
      <div class="cnc-fund-goal-col">
        <span class="cnc-fund-goal-label">Raised</span>
        <span class="cnc-fund-goal-val">$<span class="cnc-num" data-target="390" data-format="round">0</span>K</span>
      </div>
      <div class="cnc-fund-goal-col cnc-fund-goal-col-right">
        <span class="cnc-fund-goal-label">Goal</span>
        <span class="cnc-fund-goal-val">$500K</span>
      </div>
    </div>
    <div class="cnc-fund-donors">
      <div class="cnc-fund-avatar-stack">
        <div class="cnc-fund-av">🌿</div><div class="cnc-fund-av">🌱</div>
        <div class="cnc-fund-av">🍃</div><div class="cnc-fund-av">🌳</div>
      </div>
      <div class="cnc-fund-donors-text"><strong>2,841 donors</strong> have contributed<br>this month alone</div>
    </div>
  </div>
</div>
CSS
.cnc-fund { display: grid; place-items: center; padding: 32px 16px; background: #f2ede6; font-family: 'Epilogue', sans-serif; }
.cnc-fund *, .cnc-fund *::before, .cnc-fund *::after { box-sizing: border-box; }
.cnc-fund-card {
  width: 360px;
  background: #1c1208;
  border-radius: 28px;
  padding: 48px 40px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow: hidden;
}
.cnc-fund-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
  pointer-events: none;
  opacity: 0.4;
}
.cnc-fund-eyebrow { font-size: 9px; letter-spacing: 4px; text-transform: uppercase; color: #c8893a; margin-bottom: 36px; font-weight: 400; }
.cnc-fund-arc-wrap { position: relative; width: 240px; height: 130px; margin-bottom: 12px; }
.cnc-fund-arc-wrap svg { overflow: visible; }
.cnc-fund-arc-bg { fill: none; stroke: rgba(255,255,255,0.07); stroke-width: 12; stroke-linecap: round; }
.cnc-fund-arc-fill { fill: none; stroke: url(#cnc-fund-arcGrad); stroke-width: 12; stroke-linecap: round; stroke-dasharray: 534; stroke-dashoffset: 534; animation: cnc-fund-arcDraw 2.2s cubic-bezier(0.22,1,0.36,1) forwards 0.4s; filter: drop-shadow(0 0 12px rgba(255,160,50,0.6)); }
@keyframes cnc-fund-arcDraw { to { stroke-dashoffset: 117; } }
.cnc-fund-arc-center { position: absolute; bottom: 0; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; }
.cnc-fund-big-num { font-family: 'Fraunces', serif; font-size: 64px; font-weight: 700; color: #fff; line-height: 1; letter-spacing: -2px; }
.cnc-fund-big-num sup { font-size: 0.35em; font-weight: 300; vertical-align: super; letter-spacing: 0; color: #c8893a; }
.cnc-fund-pct-label { font-size: 11px; color: rgba(255,255,255,0.3); letter-spacing: 3px; text-transform: uppercase; margin-top: 6px; }
.cnc-fund-divider { width: 100%; height: 1px; background: rgba(255,255,255,0.08); margin: 32px 0 24px; }
.cnc-fund-goal-row { width: 100%; display: flex; justify-content: space-between; align-items: baseline; }
.cnc-fund-goal-col { display: flex; flex-direction: column; gap: 4px; }
.cnc-fund-goal-col-right { text-align: right; }
.cnc-fund-goal-label { font-size: 9px; letter-spacing: 3px; text-transform: uppercase; color: rgba(255,255,255,0.25); }
.cnc-fund-goal-val { font-family: 'Fraunces', serif; font-size: 26px; font-weight: 300; font-style: italic; color: #fff; letter-spacing: -0.5px; }
.cnc-fund-donors { margin-top: 28px; display: flex; align-items: center; gap: 12px; }
.cnc-fund-avatar-stack { display: flex; }
.cnc-fund-av { width: 28px; height: 28px; border-radius: 50%; border: 2px solid #1c1208; margin-left: -8px; font-size: 13px; display: flex; align-items: center; justify-content: center; background: #2e1e0a; }
.cnc-fund-av:first-child { margin-left: 0; }
.cnc-fund-donors-text { font-size: 11px; color: rgba(255,255,255,0.35); line-height: 1.4; }
.cnc-fund-donors-text strong { color: #c8893a; font-weight: 400; }
.cnc-fund-card > * { opacity: 0; animation: cnc-fund-up 0.6s ease forwards; }
.cnc-fund-eyebrow { animation-delay: 0.0s; }
.cnc-fund-arc-wrap { animation-delay: 0.1s; }
.cnc-fund-pct-label { animation-delay: 0.2s; }
.cnc-fund-divider { animation-delay: 0.3s; }
.cnc-fund-goal-row { animation-delay: 0.4s; }
.cnc-fund-donors { animation-delay: 0.5s; }
@keyframes cnc-fund-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .cnc-fund-card > *, .cnc-fund-arc-fill { animation: none; opacity: 1; stroke-dashoffset: 117; }
}
JS
(function () {
  var root = document.querySelector('.cnc-fund');
  if (!root) return;
  function easeOut(t) { return 1 - Math.pow(1 - t, 4); }
  root.querySelectorAll('.cnc-num[data-target]').forEach(function (el) {
    var target = parseFloat(el.dataset.target);
    var duration = 2200;
    var start = performance.now();
    function tick(now) {
      var t = Math.min((now - start) / duration, 1);
      var v = easeOut(t) * target;
      el.textContent = Math.round(v).toLocaleString();
      if (t < 1) requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);
  });
})();