Stats Counter Card
This is a statistics card component that is ideal for dashboards and landing pages, as the numbers animate and count up from zero as the page scrolls.
HTMLCSSJavaScript
Preview
Four statistics cards are arranged in a grid, and as you scroll, the numbers count up from zero.
0
Active Users
0%
Satisfaction
0+
Countries
0%
Uptime
Use cases and features
- Landing page performance section: Impactfully communicate metrics such as views, customer satisfaction, number of countries, etc.
- Using Intersection Observer: Animation fires when scrolled and visible for the first time.
- Decimal point support: Supports both integer and decimal counters with the
data-decimalsattribute. - Color management with CSS variables: You can use the
--accentvariable to separate accent colors for each card.
Points of design and UX
- Ease-out animation: Rather than
cubic-bezier, a unique ease-out using a cubic hidden function is a natural movement in which the numerical value stops accelerating. - Gradient text: With
-webkit-background-clip: text, the number itself becomes a gradation, creating a premium feel. - Format numbers with
toLocaleString(): Display numbers according to the locale, such as 12847 → 12,847.
How to customize
Change counting speed (milliseconds):
// Shorten animation time
// animateCounter(el, target, suffix, decimals, 1000);
// Lengthen animation time
// animateCounter(el, target, suffix, decimals, 3000);
Change decimal places:
<!-- 3 decimal places -->
<span class="stat-number" data-target="99.999" data-suffix="%" data-decimals="3">0%</span>
Implementation code
<div class="stats-grid">
<div class="stat-card stat-card-1">
<div class="stat-icon">🚀</div>
<span class="stat-number" data-target="12847" data-suffix="" data-decimals="0">0</span>
<div class="stat-label">Active Users</div>
</div>
<!-- Similar structure for other cards -->
</div>
<style>
`
.stat-number {
background: linear-gradient(135deg, var(--accent), var(--accent-light));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stat-card-1 { --accent: #6366f1; --accent-light: #8b5cf6; }
`
</style>
<script>
(function() {
function animateCounter(el, target, suffix, decimals, duration) {
var start = null;
function step(ts) {
if (!start) start = ts;
var p = Math.min((ts-start)/duration,1);
var v = (1-Math.pow(1-p,3))*target;
el.textContent=(decimals>0?v.toFixed(decimals):Math.floor(v).toLocaleString())+suffix;
if(p<1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(e) {
if (e.isIntersecting) {
var el = e.target;
if (!el.classList.contains('animated')) {
el.classList.add('animated');
animateCounter(el, parseFloat(el.dataset.target), el.dataset.suffix || '', parseInt(el.dataset.decimals || '0'), 2000);
}
}
});
}, { threshold: 0.5 });
document.querySelectorAll('.stat-number').forEach(function(el) {
observer.observe(el);
});
})();
</script> Browser compatibility status
| Browser | Compatibility status |
|---|---|
| Chrome 51+ | ✅ Fully compatible |
| Firefox 55+ | ✅ Fully compatible |
| Safari 12.1+ | ✅ Compatible |
| Edge 79+ | ✅ Fully compatible |
The Intersection Observer API supports Nenewa only in modern browsers. A polyfill is required if you want IE11 support.�����������������������������������������