Numbers roll like counter wheels. The last digit spins the most, as many turns as the change really passed, and the digits ripple from the left. Change data-value to roll again.
Numbers that roll like counter wheels. The last digit spins the most, as many extra turns as the change really passed through (at most three), and the wheels start one after another from the left, so the number ripples into place and lands with a small settle.
It rolls from the number it shows to the new one, forward when the number goes up and backward when it goes down.
Options
Attribute or property
Does
Default
data-value
The number to show. Change it to roll
Required
data-decimals
Digits after the point
0
--odo-h
Height of one digit
1.15em
--odo-pad
Room above and below each window for the fade
.18em
Style the windows through .odo-wheel and the commas and points through .odo-sep. The demo gives each wheel a tinted window.
Do
Use it for a number that changes while someone watches: takings, tickets sold, a live count.
Keep the plain number in the markup. It is what shows without the script, and what screen readers read.
Do not
Roll a number that changes every second. Each roll takes about a second and a half.
Use it for long body numbers. It is for one figure per tile.
Reduced motion
The wheels go straight to the new digits. Nothing rolls.
style.css
/*
* odometer: numbers that roll like counter wheels. odometer.js builds one wheel per digit; this
* file shapes them. Each wheel is a window one digit tall with a strip of digits behind it. The
* strip's edges fade, so a rolling digit blurs into the window edge instead of being cut by it.
* Without the script, the plain number shows and nothing moves.
*/.odometer { display: inline-flex; font-variant-numeric: tabular-nums; white-space: nowrap; }
.js .odometer[data-value]:not(.is-ready) { visibility: hidden; }
.odo-text {
position: absolute; width: 1px; height: 1px; overflow: hidden;
clip-path: inset(50%); white-space: nowrap;
}
.odo-face { display: inline-flex; align-items: flex-start; }
.odo-wheel {
position: relative;
display: inline-block;
height: var(--odo-h, 1.15em);
overflow: hidden;
-webkit-mask-image: linear-gradient(transparent, #00022%, #00078%, transparent);
mask-image: linear-gradient(transparent, #00022%, #00078%, transparent);
}
/* the mask needs room: pad the window and pull the neighbours back so the digit keeps its line */.odo-wheel { padding-block: var(--odo-pad, .18em); margin-block: calc(var(--odo-pad, .18em) * -1); height: calc(var(--odo-h, 1.15em) + var(--odo-pad, .18em) * 2); }
.odo-strip { display: block; }
.odo-strip > span { display: block; height: var(--odo-h, 1.15em); line-height: var(--odo-h, 1.15em); text-align: center; }
.odo-sep { line-height: var(--odo-h, 1.15em); }
@media (prefers-reduced-motion: reduce) {
/* odometer.js sets each wheel straight to its digit; nothing rolls */.odo-strip { transition: none; }
}