{ CF }

15 CSS Number Counter Animations

Brutalist Finance Terminal

Raw high-contrast trading-desk dashboard with a live scrolling ticker tape, a featured portfolio counter, scanline overlay and neon-yellow progress bars. IBM Plex Mono and Bebas Neue carry the brutalist tone.

CSS + JS MIT licensed

Brutalist Finance Terminal the 10th of 15 designs in the 15 CSS Number Counter 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

Open in playground

The code

<div class="cnc-bft">
  <div class="cnc-bft-grid">
    <div class="cnc-bft-ticker">
      <div class="cnc-bft-ticker-inner">
        <span>NASDAQ +2.41%</span><span>DOW +0.87%</span><span>S&amp;P500 +1.23%</span>
        <span>BTC $68,420</span><span>ETH $3,810</span><span>GOLD $2,341/oz</span>
        <span>VIX 14.2</span><span>10Y YIELD 4.38%</span><span>DXY 104.1</span>
        <span>NASDAQ +2.41%</span><span>DOW +0.87%</span><span>S&amp;P500 +1.23%</span>
        <span>BTC $68,420</span><span>ETH $3,810</span><span>GOLD $2,341/oz</span>
        <span>VIX 14.2</span><span>10Y YIELD 4.38%</span><span>DXY 104.1</span>
      </div>
    </div>
    <div class="cnc-bft-stat cnc-bft-featured">
      <span class="cnc-bft-index">01 · Portfolio Value</span>
      <div class="cnc-bft-number">
        <span class="cnc-bft-prefix">$</span>
        <span class="cnc-num" data-target="2847391">0</span>
      </div>
      <div class="cnc-bft-label">Total AUM · All Positions</div>
      <div class="cnc-bft-progress-line"><div class="cnc-bft-progress-fill"></div></div>
      <div class="cnc-bft-sub">Updated 0.3s ago · 24h change: +$48,210</div>
    </div>
    <div class="cnc-bft-stat cnc-bft-danger">
      <span class="cnc-bft-badge cnc-bft-down">▼ SHORT</span>
      <span class="cnc-bft-index">02 · Drawdown</span>
      <div class="cnc-bft-number">
        <span class="cnc-num" data-target="4.7" data-decimals="1">0</span>
        <span class="cnc-bft-suffix">%</span>
      </div>
      <div class="cnc-bft-label">Max Drawdown · 30D</div>
      <div class="cnc-bft-progress-line"><div class="cnc-bft-progress-fill"></div></div>
    </div>
    <div class="cnc-bft-stat">
      <span class="cnc-bft-badge cnc-bft-up">▲ LONG</span>
      <span class="cnc-bft-index">03 · Win Rate</span>
      <div class="cnc-bft-number">
        <span class="cnc-num" data-target="78">0</span>
        <span class="cnc-bft-suffix">%</span>
      </div>
      <div class="cnc-bft-label">Closed Trades · YTD</div>
      <div class="cnc-bft-progress-line"><div class="cnc-bft-progress-fill"></div></div>
    </div>
    <div class="cnc-bft-stat">
      <span class="cnc-bft-index">04 · Sharpe Ratio</span>
      <div class="cnc-bft-number">
        <span class="cnc-num" data-target="2.38" data-decimals="2">0</span>
      </div>
      <div class="cnc-bft-label">Risk-Adjusted Return</div>
      <div class="cnc-bft-progress-line"><div class="cnc-bft-progress-fill"></div></div>
    </div>
    <div class="cnc-bft-stat">
      <span class="cnc-bft-index">05 · Open Positions</span>
      <div class="cnc-bft-number">
        <span class="cnc-num" data-target="312">0</span>
      </div>
      <div class="cnc-bft-label">Across 14 Exchanges</div>
      <div class="cnc-bft-progress-line"><div class="cnc-bft-progress-fill"></div></div>
    </div>
  </div>
</div>
.cnc-bft {
  --bft-black: #0a0a0a;
  --bft-white: #f0ede8;
  --bft-yellow: #ffe600;
  --bft-red: #ff2d2d;
  --bft-gray: #1c1c1c;
  --bft-mid: #333;
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 40px 20px;
  background: var(--bft-black);
  color: var(--bft-white);
  font-family: 'IBM Plex Mono', monospace;
  overflow: hidden;
}
.cnc-bft::before {
  content: '';
  position: absolute; inset: 0;
  background: repeating-linear-gradient(
    0deg, transparent, transparent 2px,
    rgba(0,0,0,0.15) 2px, rgba(0,0,0,0.15) 4px);
  pointer-events: none; z-index: 100;
}
.cnc-bft-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  width: 980px;
  border: 3px solid var(--bft-white);
}
.cnc-bft-stat {
  border: 1.5px solid var(--bft-mid);
  padding: 36px 28px 28px;
  position: relative; overflow: hidden;
  transition: background 0.1s;
}
.cnc-bft-stat:hover { background: var(--bft-gray); }
.cnc-bft-stat::after {
  content: ''; position: absolute;
  bottom: 0; left: 0; width: 100%; height: 3px;
  background: var(--bft-yellow);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.4s ease;
}
.cnc-bft-stat:hover::after { transform: scaleX(1); }
.cnc-bft-featured {
  grid-column: span 2;
  background: var(--bft-yellow);
  color: var(--bft-black);
  border-color: var(--bft-yellow);
}
.cnc-bft-featured .cnc-bft-label,
.cnc-bft-featured .cnc-bft-sub { color: var(--bft-black); opacity: 0.6; }
.cnc-bft-danger { border-top: 4px solid var(--bft-red); }
.cnc-bft-index {
  font-size: 10px; letter-spacing: 3px; text-transform: uppercase;
  opacity: 0.35; margin-bottom: 20px; display: block;
}
.cnc-bft-number {
  font-family: 'Bebas Neue', sans-serif;
  font-size: clamp(56px, 7vw, 88px);
  line-height: 1; letter-spacing: -1px;
  display: flex; align-items: baseline; gap: 4px;
}
.cnc-bft-featured .cnc-bft-number { font-size: clamp(72px, 9vw, 120px); }
.cnc-bft-prefix, .cnc-bft-suffix {
  font-size: 0.38em;
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 700; letter-spacing: 2px;
  align-self: flex-end; padding-bottom: 0.15em;
}
.cnc-bft-label {
  font-size: 10px; letter-spacing: 4px; text-transform: uppercase;
  opacity: 0.45; margin-top: 12px;
}
.cnc-bft-sub {
  font-size: 11px; opacity: 0.35; margin-top: 6px; letter-spacing: 1px;
}
.cnc-bft-badge {
  position: absolute; top: 14px; right: 14px;
  font-size: 9px; letter-spacing: 2px; text-transform: uppercase;
  padding: 3px 8px; border: 1px solid currentColor; opacity: 0.5;
}
.cnc-bft-up { color: #39ff14; }
.cnc-bft-down { color: var(--bft-red); }
.cnc-bft-ticker {
  grid-column: span 3;
  background: var(--bft-yellow);
  color: var(--bft-black);
  padding: 8px 0; overflow: hidden; position: relative;
  font-size: 11px; letter-spacing: 3px;
  text-transform: uppercase; font-weight: 700;
}
.cnc-bft-ticker-inner {
  display: flex; gap: 60px;
  animation: cnc-bft-ticker 18s linear infinite;
  white-space: nowrap; width: max-content;
}
@keyframes cnc-bft-ticker {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.cnc-bft-progress-line {
  height: 2px; background: rgba(255,255,255,0.08);
  margin-top: 16px; position: relative; overflow: hidden;
}
.cnc-bft-progress-fill {
  height: 100%; background: var(--bft-yellow);
  animation: cnc-bft-fillBar 1.5s ease forwards;
  transform-origin: left; transform: scaleX(0);
}
.cnc-bft-featured .cnc-bft-progress-fill { background: var(--bft-black); }
@keyframes cnc-bft-fillBar { to { transform: scaleX(1); } }
.cnc-bft-stat:nth-child(2) .cnc-bft-progress-fill { animation-delay: 0.3s; width: 73%; }
.cnc-bft-stat:nth-child(3) .cnc-bft-progress-fill { animation-delay: 0.5s; width: 91%; }
.cnc-bft-stat:nth-child(4) .cnc-bft-progress-fill { animation-delay: 0.7s; width: 47%; }
.cnc-bft-stat:nth-child(5) .cnc-bft-progress-fill { animation-delay: 0.9s; width: 85%; }
.cnc-bft-stat:nth-child(6) .cnc-bft-progress-fill { animation-delay: 1.1s; width: 62%; }
@media (prefers-reduced-motion: reduce) {
  .cnc-bft-ticker-inner, .cnc-bft-progress-fill { animation: none; }
}
(function () {
  function easeOut(t) { return 1 - Math.pow(1 - t, 4); }
  document.querySelectorAll('.cnc-bft .cnc-num').forEach(function (el) {
    var target = parseFloat(el.dataset.target);
    var decimals = parseInt(el.dataset.decimals || 0, 10);
    var duration = 1800;
    var start = performance.now();
    function tick(now) {
      var t = Math.min((now - start) / duration, 1);
      var val = easeOut(t) * target;
      el.textContent = decimals > 0
        ? val.toFixed(decimals)
        : Math.floor(val).toLocaleString();
      if (t < 1) requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);
  });
})();

Search CodeFronts

Loading…