Count Up
A copy-paste stats & counters component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
Stats & CountersHTMLCSSJavaScriptany framework
2847Total users
Copy into your project
HTML
<!-- Count Up — initial value of 0 will animate up to data-target -->
<div class="nuda-count-up">
<span class="nuda-count-up__value" data-target="2847">0</span>
<span class="nuda-count-up__label">Total users</span>
</div>CSS
/* Count Up
Big stat number with a label. JS does the count-up.
Customize: --count-text, --count-label */
.nuda-count-up {
--count-text: #fafafa;
--count-label: #63636e;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
padding: 12px;
}
.nuda-count-up__value {
font: 700 2rem ui-sans-serif, system-ui, sans-serif;
color: var(--count-text);
font-variant-numeric: tabular-nums;
line-height: 1;
background: linear-gradient(180deg, #fafafa, #a1a1aa);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.nuda-count-up__label {
font: 500 0.625rem ui-sans-serif, system-ui, sans-serif;
color: var(--count-label);
text-transform: uppercase;
letter-spacing: 0.08em;
}JavaScript
/* Count Up — animates from 0 to data-target.
Triggers when the element scrolls into view. */
(function () {
var els = document.querySelectorAll('.nuda-count-up__value');
if (!els.length) return;
function animate(el) {
var target = parseInt(el.dataset.target, 10) || 0;
var duration = 1400;
var start = performance.now();
function tick(now) {
var t = Math.min(1, (now - start) / duration);
// easeOutCubic
var eased = 1 - Math.pow(1 - t, 3);
el.textContent = Math.round(target * eased).toLocaleString();
if (t < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
animate(e.target);
io.unobserve(e.target);
}
});
}, { threshold: 0.4 });
els.forEach(function (el) { io.observe(el); });
} else {
els.forEach(animate);
}
})();How to use Count Up
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.