Skip to content
Protocore Design Systemv1.6.9

/// Data Display

RollingNumber

A tabular, monospaced counter whose digits roll like an odometer to a new value, honoring reduced motion and Intl formatting.

import { RollingNumber } from "@protocore/pds";
View as Markdown

Live counter

Change value and each digit column rolls from its old glyph to the new one. The animation is pure CSS transform — the component server-renders the final value, so it's correct with no JS and snaps instantly under reduced motion.

8,241,003

Digits roll from the old value to the new one.

Formatting

Pass Intl.NumberFormat options through format — compact notation, currency, fixed fraction digits — and a locale. Grouping separators and currency symbols render as static glyphs between the rolling digit columns.

MRR (EUR, compact)

€49.9K

Requests / min

1.2K

Signal number

Set signal to paint the number in the reserved accent — the one live figure on a console. Use it sparingly; the accent is a scarce signal, not a decoration.

ACTIVE SESSIONS

2,048

TOTAL (static)

1,284,301

How the roll works

Each ASCII digit is an odometer column: a 0–9 strip clipped to a one-glyph window, translated on the Y axis to show the current digit. When value changes, React re-renders the new translate offset and CSS transitions the strip — so 3 → 7 visibly rolls through 4, 5, 6. The value is also emitted once as visually-hidden text, so assistive tech reads the number, not the digit ladder. There is no rAF loop; the animation is declarative and therefore SSR-safe and reduced-motion-safe (the global motion rule collapses the transition to a snap).

Usage

Do

  • Use it for a single, prominent live figure — active sessions, block height, throughput.
  • Format with `Intl` options rather than pre-formatting the string yourself.
  • Give it an `aria-label` (or nearby label) describing what the number measures.
  • Reserve `signal` for the one number that is genuinely live on the surface.

Don't

  • Don't roll a whole table of numbers — the motion stops being a signal and becomes noise.
  • Don't feed it a pre-formatted string; pass the raw `number` and a `format`.
  • Don't use `signal` as a general highlight — the accent budget is under 1%.
  • Don't animate values that update many times per second; throttle upstream first.

Accessibility

  • The rolling digit columns are `aria-hidden`; the formatted value is exposed once as visually-hidden text so screen readers announce the number cleanly.
  • Under `prefers-reduced-motion: reduce` the roll collapses to an instant snap via the design system's base motion rule — the final value is always shown.
  • Server-rendering emits the final value, so the number is correct before (and without) hydration.

RollingNumber props

PropTypeDefaultDescription
classNamestring
durationnumber800Roll duration in milliseconds. Ignored under reduced motion (snaps).
formatNumberFormatOptions`Intl.NumberFormat` options — currency, compact notation, min/max fraction digits, etc. The formatted string is rolled digit-by-digit.
localestringenBCP-47 locale, fixed so SSR and hydration agree.
signalbooleanPaint the number in the reserved accent — the single-live-number signal role.
styleCSSProperties
value *numberThe number to display. Digits roll from the previous value to this one.

Related

  • MoneyAmountA deterministic currency renderer — mono tabular figures, minor-unit aware, compact and sign-colored.
  • MetricDeltaA compact directional change indicator — a ▲▼ glyph plus a signed magnitude, toned by direction.
  • SignalCountTHE green number — a single bracketed accent count with tabular figures, one per screen.
  • SparklineA tiny inline-SVG trend line — currentColor stroke, optional area wash and an accent signal dot.