Number Roll
A copy-paste countdowns component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
CountdownsHTMLCSSJavaScriptany framework
0123456789
0123456789
0123456789
Copy into your project
HTML
<div class="nuda-number-roll" role="timer" aria-label="Counter">
<div class="nuda-number-roll__digit">
<div class="nuda-number-roll__strip" data-target="3">
<span>0</span><span>1</span><span>2</span><span>3</span><span>4</span>
<span>5</span><span>6</span><span>7</span><span>8</span><span>9</span>
</div>
</div>
<div class="nuda-number-roll__digit">
<div class="nuda-number-roll__strip" data-target="7">
<span>0</span><span>1</span><span>2</span><span>3</span><span>4</span>
<span>5</span><span>6</span><span>7</span><span>8</span><span>9</span>
</div>
</div>
<div class="nuda-number-roll__digit">
<div class="nuda-number-roll__strip" data-target="2">
<span>0</span><span>1</span><span>2</span><span>3</span><span>4</span>
<span>5</span><span>6</span><span>7</span><span>8</span><span>9</span>
</div>
</div>
</div>CSS
/* Number Roll
Slot-machine style digit roll animation.
Customize: --roll-bg, --roll-text, --roll-digit-size */
.nuda-number-roll {
--roll-bg: #1a1a1a;
--roll-text: #fff;
--roll-digit-height: 40px;
--roll-digit-width: 32px;
display: flex;
gap: 2px;
}
.nuda-number-roll__digit {
width: var(--roll-digit-width);
height: var(--roll-digit-height);
background: var(--roll-bg);
border-radius: 6px;
overflow: hidden;
position: relative;
}
.nuda-number-roll__strip {
display: flex;
flex-direction: column;
transition: transform 1.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.nuda-number-roll__strip span {
width: var(--roll-digit-width);
height: var(--roll-digit-height);
display: flex;
align-items: center;
justify-content: center;
color: var(--roll-text);
font-size: 1.2rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
.nuda-number-roll__strip {
transition: none;
}
}JavaScript
/* Number Roll — Rolls digits to a target value.
Set data-target on each .nuda-number-roll__strip element. */
(function () {
var strips = document.querySelectorAll('.nuda-number-roll__strip');
if (!strips.length) return;
strips.forEach(function (strip, i) {
var target = parseInt(strip.getAttribute('data-target') || '0', 10);
var digitH = strip.querySelector('span').offsetHeight;
// Slight stagger per digit
setTimeout(function () {
strip.style.transform = 'translateY(-' + (target * digitH) + 'px)';
}, i * 150);
});
})();How to use Number Roll
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.