Skip to content
All Taste components

Text effects

Rolling digits

An odometer-style figure whose digits spin and land one after another on the value.

currency · separators · trend badge

Revenue this month+12.4%
vs $42,960 in September
/* globals.css
@keyframes digit-roll { from { transform: translateY(0); } }
*/
import { cn } from "@/lib/utils";

const STRIP = "01234567890123456789".split("");

/** value is the formatted string, e.g. "$48,290" — non-digits stay still. */
export function RollingNumber({ value, className }: { value: string; className?: string }) {
  let column = 0;
  return (
    <span role="img" aria-label={value} className={cn("inline-flex text-[40px] font-medium leading-[1.1] tracking-tight text-fg tabular-nums", className)}>
      {value.split("").map((ch, i) => {
        if (!"0123456789".includes(ch)) return <span key={i} aria-hidden>{ch}</span>;
        const delay = 150 + column++ * 90;
        return (
          <span key={i} aria-hidden className="inline-block h-[1.1em] overflow-hidden">
            <span
              className="flex flex-col animate-[digit-roll_1.7s_cubic-bezier(.16,1,.3,1)_both] motion-reduce:animate-none"
              style={{ transform: "translateY(" + -(10 + Number(ch)) * 5 + "%)", animationDelay: delay + "ms" }}
            >
              {STRIP.map((d, k) => <span key={k}>{d}</span>)}
            </span>
          </span>
        );
      })}
    </span>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.