[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-95976":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":9,"pushedAt":9,"updatedAt":27,"readmeContent":28,"aiSummary":29,"trendingCount":14,"starSnapshotCount":14,"syncStatus":12,"lastSyncTime":30,"discoverSource":31},95976,"rolling-number","kitlangton\u002Frolling-number","kitlangton","Interruptible, locale-aware rolling numbers for the web. TypeScript core and React adapter.",null,"TypeScript",112,2,1,0,4,1.43,"MIT License",false,"main",true,[22,23,24,25,26],"animation","odometer","react","typescript","web-animations","2026-09-21 02:04:29","# Rolling Number\n\n**Numbers that move without losing their place.**\n\nA small, original TypeScript library for interruptible rolling numbers. Native\nbrowser animation playback, a framework-independent DOM API, and thin React and\nSolid adapters. MIT licensed. No runtime dependencies in the DOM core.\n\n[Demo](https:\u002F\u002Frolling.kitlangton.dev) · [Contributing](CONTRIBUTING.md) · [Agent guide](AGENTS.md) · [MIT license](LICENSE)\n\n## Install\n\n```sh\nbun add @kitlangton\u002Frolling-number\n```\n\nOr use `npm install @kitlangton\u002Frolling-number`. The unscoped name belongs to\nanother project. Import the stylesheet alongside the DOM, React, or Solid entrypoint.\n\n### For coding agents\n\nThe site serves these docs as Markdown: [rolling.kitlangton.dev\u002Fllms.txt](https:\u002F\u002Frolling.kitlangton.dev\u002Fllms.txt)\nindexes them, [rolling.kitlangton.dev\u002Findex.md](https:\u002F\u002Frolling.kitlangton.dev\u002Findex.md)\nis this README, and the page itself answers `Accept: text\u002Fmarkdown` with the same\ncontent.\n\n## Try it\n\n```sh\ngit clone https:\u002F\u002Fgithub.com\u002Fkitlangton\u002Frolling-number.git\ncd rolling-number\nbun install\nbun run dev\n```\n\nThe main number shows elapsed milliseconds since opening the page. The demo also\nincludes prices, large integers, typography controls, locale changes and reduced\nmotion. Nothing needs a remote font or an API key.\n\n## React\n\n```tsx\nimport { RollingNumber } from '@kitlangton\u002Frolling-number\u002Freact'\nimport '@kitlangton\u002Frolling-number\u002Fstyles.css'\n\n\u003CRollingNumber\n  value={1234.56}\n  locales=\"en-US\"\n  format={{ style: 'currency', currency: 'USD' }}\n  duration={500}\n\u002F>\n```\n\nReact owns the accessible formatted text; the engine owns a separate decorative\nmount. There are no per-frame React state updates. Server rendering produces\nreadable text and the initial hydration does not animate. Use identical initial\nvalues, locales and options on the server and client; differing ICU\u002FCLDR versions\ncan still produce different formatted text. Hydration warnings are not suppressed.\n\nReact 18 and 19 are supported. React is an optional peer dependency; vanilla users\ndo not need to install it. The React entrypoint preserves its `use client` boundary.\n\n## Solid\n\n```tsx\nimport { createSignal } from 'solid-js'\nimport { RollingNumber } from '@kitlangton\u002Frolling-number\u002Fsolid'\nimport '@kitlangton\u002Frolling-number\u002Fstyles.css'\n\nfunction Balance() {\n  const [value, setValue] = createSignal(1234.56)\n  return \u003CRollingNumber value={value()} locales=\"en-US\" format={{ style: 'currency', currency: 'USD' }} \u002F>\n}\n```\n\nSolid 1.9+ is supported. Pass reactive props normally; the adapter forwards changes\nto the same DOM controller and destroys it on cleanup. Use Solid's `class` and\n`ref` props. Server rendering keeps readable text, and hydration adopts it without\nan initial roll. React and Solid are optional peers; each adapter imports only its\nown framework. The shipped Solid entry works in browser and server builds without\na package-specific JSX transform.\n\n## Rolling text\n\nFor labels and headings, use a direct transition: each glyph rolls straight to its\nreplacement, including lowercase letters and emoji. Newly added letters reveal\nfrom below. This does not enumerate an alphabet or animate every intermediate word.\nThe initial render stays static for SSR; subsequent text changes animate.\n\n```tsx\nimport { RollingText } from '@kitlangton\u002Frolling-number\u002Freact'\nimport '@kitlangton\u002Frolling-number\u002Fstyles.css'\n\n\u003CRollingText text={status} transition=\"direct\" stagger=\"start\" motionBlur \u002F>\n```\n\nThe same options work with `RollingText` from `\u002Fsolid` and\n`createRollingText(element, { text: status, transition: 'direct' })` from the DOM\nentrypoint. Direct transitions retain at most the visible pair plus the newest\nglyph when interrupted. They use roll mode; combining `transition=\"direct\"` with\n`mode=\"flap\"` throws. Existing alphabet-wheel behavior remains the default\n`transition=\"wheel\"`, and `charset` applies only to that behavior. RTL text keeps\nthe readable static fallback.\n\n### Alphabet wheels and split-flap boards\n\n```tsx\nimport { RollingText } from '@kitlangton\u002Frolling-number\u002Freact'\n\n\u003CRollingText text={\"FIX THE FIX\".padEnd(15)} mode=\"flap\" stagger=\"start\" motionBlur \u002F>\n```\n\n`RollingText` (also exported from `\u002Fsolid`, and `createRollingText` from the DOM\ncore) treats each character as a wheel. Characters in `charset` (default:\nspace, A–Z, 0–9 and common punctuation, exported as `FLAP_CHARSET`) advance\nthrough the wheel like a departure board; other glyphs crossfade in place. Pass an\narray to give each position its own drum, e.g. digit drums for a time and letter\ndrums for a destination. Words of different length open and close width with the\nsame layout spring as numbers. With auto direction, wheels advance, so `Z` to `A`\ntravels forward through the remaining wheel rather than backwards.\n\n`mode=\"flap\"` (numbers too) replaces the gliding reel with real split-flap\nmechanics: one card per face hinges at the slot's midline, the top half of the\ncurrent face falls, then the bottom half of the next lands, at a mechanical\n45–110 ms cadence derived from `duration`, or an explicit `flipDuration` per card.\nFor example, use `flipDuration={220}` to make individual clock ticks more visible.\nFour temporary half-card strips reuse\nthe same hinge throughout the change; native stepped timing advances their glyphs.\nTravel is bounded to one revolution, and settlement returns to one static face.\nNew characters flap in from the blank face.\n`--rn-crease` sets the visible hinge gap. Opt into `motionBlur` for vertical-only\nsmear as each half turns; landed faces stay sharp. `--rn-blur: 0` disables the smear.\nFlap faces need an opaque surface to hide the waiting glyph. The default is the\nsystem `Canvas` color; set `--rn-flap-background` or an opaque `.rn-face` background\nto match your design. Ordinary rolling mode keeps its transparent background.\n`stagger=\"start\"` or `\"end\"` sweeps a row left to right or right to left, for\nin-place changes as well as new characters.\n\nThe board demo also has an opt-in **Sound** toggle. Web Audio synthesizes grouped\ntick–clack impacts from the moving drums' native timing. It uses no recordings,\ncaps concurrent voices and volume, and mutes on reduced motion or a hidden tab.\nSound belongs to the demo, not the library or its adapters.\n\nFor a physical board, reserve its slots in the text: comment count `3` uses `\" 3\"`\nwith a blank-capable tens drum, and shorter PR titles use trailing blanks up to the\nlongest title. This keeps the same cards in place instead of inserting digits or\nresizing the display. The standalone flap-board experiment uses fictional PR data;\nit is separate from the number showcase and is not connected to GitHub.\n\n## Vanilla DOM\n\n```ts\nimport { createRollingNumber } from '@kitlangton\u002Frolling-number'\nimport '@kitlangton\u002Frolling-number\u002Fstyles.css'\n\nconst counter = createRollingNumber(document.querySelector('#balance')!, {\n  value: 1234.56,\n  locales: 'en-US',\n  format: { style: 'currency', currency: 'USD' },\n})\n\ncounter.update({ value: 1300 })\ncounter.refresh() \u002F\u002F Explicit refresh after a theme or variable-font change\ncounter.finish()  \u002F\u002F Immediately show the latest target\ncounter.destroy() \u002F\u002F Releases resources; leaves the final formatted text\n```\n\nThe controller owns the host's children until destruction. `destroy()` is\nidempotent. Invalid values\u002Foptions throw before replacing the current display.\n\n### Options\n\n| Option | Default | Behavior |\n| --- | --- | --- |\n| `value` | required | `number` or `bigint`; never parsed from display text |\n| `locales` | browser default | Locale(s) passed to `Intl.NumberFormat` |\n| `format` | `{}` | Native `Intl.NumberFormatOptions` |\n| `duration` | `500` | Milliseconds; `0` disables motion; maximum `10000` |\n| `flipDuration` | automatic | Milliseconds per card in flap mode, from `1` to `10000`; leaves roll-mode timing unchanged |\n| `animated` | `true` | `false` immediately settles the latest value |\n| `motionBlur` | `false` | Opt-in vertical blur on rolling digits and turning flap halves |\n| `direction` | `\"auto\"` | `\"auto\"`, `\"up\"`, or `\"down\"` |\n| `pauseOffscreen` | `true` | Offscreen counters keep the latest text without rolling |\n\nAuto direction follows **displayed magnitude**: `-12 → -11` rolls `12 → 11`, with\nthe sign handled separately. Large jumps have bounded travel; the renderer does\nnot enumerate every intervening numerical value. Unchanged formatted values do\nnot restart animations.\n\nThe React component additionally accepts ordinary span attributes, including\n`className`, `style`, `aria-label`, and an element ref. It does not accept children\nor raw HTML. Set `animated={false}` for updates that should settle immediately.\nChanges between supported formats animate digits, separators and symbols while\nrespecting reduced-motion preferences.\n\nFor prominent counters, opt into `motionBlur`. Fast reels crossfade into a vertical\nSVG blur, then sharpen as they slow down. Stable digits and punctuation stay sharp.\nThe temporary duplicate reel and native opacity effects are removed on settlement;\ndisabling the option clears active blur immediately. This adds paint\u002FDOM work and\nis not a performance optimization. The showcase and examples enable it; library\ncounters default to no blur. Reduced motion disables the effect along with rolling.\nNew digits can also smear vertically during their eased entrance; the blend clears\nat settlement. Currency signs, separators and other symbols crossfade instead of\nrolling. Replaced symbols keep their semantic position rather than moving through\nthe adjacent digits, with a small 4% scale accent during replacement.\n\nWhen a value grows or shrinks by several places at once, the new digits and their\nseparators cascade outward from the digits already on screen, one short step each.\n`stagger` selects the order: `\"outward\"` (default), `\"start\"` or `\"end\"` for a\nboard-style sweep from either edge, or `\"none\"`.\nThe whole cascade stays inside a third of the duration, so it reads as one update rather\nthan a typing effect, and interruptions still sample the current position of every\nplace.\nSee [the scoped blur-cost measurement](perf\u002Fblur-cost.md) for its overhead and limits.\n\n### Styling hooks\n\n- `data-rn-trend=\"up\" | \"down\" | \"none\"` is set on the host for every change, so\n  CSS can tint or weight a number by direction without any JavaScript.\n- `--rn-blur` (default `1`) scales the optional motion blur per counter; set it on\n  the host or any ancestor. It is read during measurement, never during playback.\n- `--rn-mask` and `--rn-edge-fade` control the reel's soft top and bottom edges;\n  `--rn-crease` is the hinge gap between split-flap halves (default `1px`).\n- Wheel slots carry `data-rn-wheel` (and `data-rn-flap` in flap mode); symbol\n  slots do not.\n\n## How it stays small and stable\n\n- **One numeral per digit at rest.** During a roll, only a bounded travel strip\n  exists; completion returns to one face. Huge value changes do not create huge reels.\n- **Native playback.** Critically damped spring trajectories are sampled once into\n  a `linear()` easing between two direct transform keyframes where supported.\n  Explicit sampled keyframes remain the compatibility fallback. No JavaScript\n  animation-frame loop runs during playback.\n- **Interruptions replace, not accumulate.** A new target samples the current\n  position and velocity; each property has one owning animation.\n- **Batched geometry.** Across counters, reads happen before animation writes.\n  ResizeObserver tracks intrinsic boxes and individual glyph sizes; font-loading\n  events and `refresh()` handle further invalidation.\n- **Readable by default.** Reduced motion, unsupported animation APIs, offscreen\n  state, and non-rollable formats retain an intact formatted text value.\n\n### Typography and layout contract\n\nFonts, size, weight, style and spacing are inherited. Proportional numerals work;\n`font-variant-numeric: tabular-nums` is optional, not a measurement substitute.\n\nDigit viewports have a linear alpha fade at their top and bottom edges. Tune it\nwithout changing the measurement or animation:\n\n```css\n.counter { --rn-edge-fade: 0.12em; } \u002F* default *\u002F\n.counter--hard-clip { --rn-mask: none; } \u002F* opt out of masking *\u002F\n```\n\n**The host adopts its target intrinsic width immediately; internal glyphs glide\nto their target positions.** The renderer captures the previous and next origins\nin shared measurement batches, keeping existing glyphs continuous in left-, center-\nand right-aligned layouts. New glyphs rise from below after space starts opening.\nThis does not animate arbitrary surrounding siblings\nor promise zero layout shift. Reserve space with CSS `min-width` when a stable\nsurrounding layout matters. Ancestor axis-aligned scaling is supported; rotated or\nskewed ancestors, vertical writing and per-digit typography are not supported.\n\nKeep horizontal overflow visible on containers around the number. A horizontal\nscrollport or `overflow: hidden` can cut off outgoing digits when the host shrinks,\neven when the final value fits. The demo leaves both its bento tiles and number\ncontainers unclipped; the renderer handles vertical reel clipping separately.\n\n### Locale and accessibility boundaries\n\nAll values use native Intl formatting, including bigint, negative zero, accounting\nsigns, percentages and alternate grouping. **Rolling currently targets standard\nLatin-digit formats in LTR layout.** RTL surroundings\u002Fscripts, non-Latin digits, compact\u002Fscientific\u002Fengineering\nnotation, NaN and infinity render as intact static localized text. They are not\nsilently transliterated or forced into LTR layout.\n\nAssistive technology receives one formatted value; decorative glyphs are hidden.\nThe native value is selectable and copyable, including at rest. Copying uses the\nlatest formatted target once, not the temporary reel glyphs; while motion is in\nprogress that target may differ from the intermediate painted digits.\nThere is no default live region. Applications can opt into `aria-live=\"polite\"`\nand `aria-atomic=\"true\"` for a deliberately paced announcement. Reduced-motion\nchanges settle active animations immediately.\n\n## Performance, without the superlatives\n\nNumberFlow is the inspiration and the comparison target, not copied source.\nThe benchmark pins **NumberFlow 0.6.2**, measures production code, includes a\nplain-text floor, counts shadow-DOM elements, and reports repeated measurements.\n\n```sh\nbun run bench\n```\n\nSee [the methodology](perf\u002Fmethod.md) and [research and design tradeoffs](docs\u002Fresearch.md).\nBenchmarks are workload- and browser-specific. A smaller DOM or no per-frame\nJavaScript does not, on its own, prove smoother presented frames or universal speed.\n\nThe [latest local DOM comparison](perf\u002Fcurrent.md) measured **59.8% less\nmain-thread work and 56.7% fewer retained elements** than NumberFlow 0.6.2 for\n100 synchronized counters in Chromium 151 on an Apple M2 Max. Both libraries had\nlong frame intervals in this stress workload. The report includes the source\nfingerprint, spread, limits, and raw runs—not a claim that every application or\nbrowser is faster. [Earlier results](perf\u002Fresults.md) remain for historical context.\n\n## Development\n\nStart with [CONTRIBUTING.md](CONTRIBUTING.md) for setup, focused tests, PR guidance,\nand the distinction between library releases and website deployment. The short\n[AGENTS.md](AGENTS.md) map covers rendering invariants and validation expectations.\n\n```sh\nbun run check\nbunx playwright install chromium firefox webkit\nbun run test:browser\nbun run build:demo\n```\n\nTests cover formatting, exact bigint handling, interruption continuity, bounded\ncleanup, proportional fonts, reduced motion, hidden → visible transitions, and\nReact hydration under StrictMode and Solid hydration\u002Freactive cleanup in Chromium,\nFirefox and WebKit.\n\nThe dark-only demo includes a fictional tee shop, a hold-to-charge Super Like,\nteam seats, a BigInt event viewer, upload, weather, invoice, audience, Scrub, and\ndirect word transitions. Their SVG illustrations are local and original;\nno purchases, uploads or external account changes take place. The header is a plain\nwordmark. Buying the demo tee brightens revenue, then fades back over 1.8 seconds;\nreduced motion disables that flash. The examples do not re-render on every hero tick.\n\nThe unlisted `\u002Fbenchmarks.html` page compares the React integrations of Rolling\nNumber, NumberFlow, React Animated Numbers, and React CountUp. It is not linked in\nthe showcase navigation and asks crawlers not to index it; this is not access\ncontrol. Its in-browser elapsed\u002FrAF measurements are separate from the CLI's DOM\ntask-time benchmark. CountUp is numeric interpolation, not a glyph-roll equivalent.\n\n`dist\u002F` contains ESM and declarations plus an explicit stylesheet. There is no\nautomatic global style injection, custom-element registration, or server-side DOM\naccess. See [LICENSE](LICENSE).\n\n### Website deployment\n\n`bun run deploy` builds the showcase, then deploys static assets to\nCloudflare Workers at [rolling.kitlangton.dev](https:\u002F\u002Frolling.kitlangton.dev).\nWrangler uses the operator's Cloudflare login; no credentials belong in the repo.\n","这是一个用于网页端的可中断、本地化感知的滚动数字动画库。核心功能包括基于原生 Web Animations API 的高性能数字滚动动画，支持中断\u002F暂停\u002F重置，内置国际化（Intl.NumberFormat）格式化能力，并提供 React 和 Solid 框架适配器；其 DOM 核心无运行时依赖，支持服务端渲染与无障碍访问。适用于需要动态展示数值变化的场景，如数据看板、金融仪表盘、实时统计计数器、价格变动提示等，尤其适合对动画可控性、可访问性和多语言支持有要求的前端项目。","2026-09-08 02:30:03","CREATED_QUERY"]