For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. The dithercn agent skill, for building any dither on the engine, is at /.well-known/agent-skills/dithercn/SKILL.md.
dithercn

Dither number

An animated number for KPI tiles and landing stats: tabular digits that roll or resolve through a dither, with Intl formatting, prefix and suffix, colour and size presets.

Controls
Effect
Format
Accent
Duration900ms
Stagger40ms

Installation

pnpm dlx shadcn@latest add https://dithercn.dev/r/dither-number.json

Usage

import { DitherNumber } from "@/components/dithercn/dither-number";
 
export function Stat() {
  return <DitherNumber value={128406} size="xl" />;
}

When the number scrolls into view, every digit moves from from (0 by default) to its place in value. Change value later and only the digits that differ move, left to right, stagger milliseconds apart. While a digit moves it takes the accent colour, then fades back to the number's colour as it lands.

There are two effects:

  • roll spins each digit on a strip, up when the value grows and down when it shrinks. The top and bottom of each digit's window dither out, so digits fade in and out of the line instead of being cut off. A place that moves by ten or more spins a full extra turn, like a mechanical counter.
  • resolve turns the old digit into the new one cell by cell. The two use complementary dither masks, so each cell shows one digit or the other and they never blur together.

Digits are tabular, so the width only changes when the number of digits does. Separators, currency symbols, the prefix and the suffix keep their natural width and never move.

Examples

KPI tiles

A dashboard row whose values update every few seconds. size="sm", a muted suffix through affixClassName, and resolve and roll side by side. The latency tile uses an orange accent.

Landing stat

size="display" scales with the viewport. A longer duration and stagger make the first roll feel heavier; after that, the counter ticks up live and only the last few digits move.

Props

PropTypeDefault
value*

The number to show. Change it and the digits move to the new value.

Type: numberDefault –
from

Where the first run starts.

Type: numberDefault 0
format

Currency, percent, compact notation, decimals and so on.

Type: Intl.NumberFormatOptionsDefault –
locales

Passed to Intl.NumberFormat. Set it when the server and client must format the same way.

Type: Intl.LocalesArgumentDefault The visitor's locale
prefix

Text before the number, such as "+".

Type: stringDefault –
suffix

Text after the number, such as " ms".

Type: stringDefault –
affixClassName

Classes for the prefix and suffix, for example a smaller, muted unit.

Type: stringDefault –
effect

Digits spin on a strip, or turn into the new digit cell by cell.

Type: "roll" | "resolve"Default "roll"
trigger

Start the first run on mount, or when scrolled into view.

Type: "mount" | "in-view"Default "in-view"
size

A type preset: 2xl, 4xl, 6xl, 8xl, or a fluid display size. Leave it unset to size the number with your own classes.

Type: "sm" | "md" | "lg" | "xl" | "display"Default –
color

Colour of the number: a palette name or any CSS colour.

Type: DitherColorDefault the text colour
accent

Colour a digit takes while it moves, fading back as it lands.

Type: DitherColor | "none"Default "ink"
duration

Milliseconds per change, for one digit.

Type: numberDefault 900 (roll), 560 (resolve)
stagger

Milliseconds between one digit starting and the next.

Type: numberDefault 40
matrix

The threshold pattern for the dithered edges and the resolve.

Type: "bayer2" | "bayer4" | "bayer8"Default "bayer4"
cell

Size of one dither cell in CSS pixels, at least 1.

Type: numberDefault font size / 30

Notes

  • The formatted value, with its prefix and suffix, is real text laid over the animation and made transparent, so it can be selected and copied and is what screen readers and search engines read. The moving digits are hidden from assistive technology. For a value that updates live, add aria-live="polite" yourself.
  • The number sets line-height: 1.1 so the dithered fade has room above and below the digits. A tighter leading-* class works, but the fade may touch the tops of tall glyphs.
  • The server renders the final value. On the client the digits wait at from until the trigger fires.
  • With reduced motion turned on, the number changes instantly.
  • Only ASCII digits animate. Formats that use another numbering system show their digits without motion.