npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sfinterface/numbers

v0.3.4

Published

Numbers that move the way they should. A React component from The San Francisco Interface.

Readme

@sfinterface/numbers

Numbers that move the way they should. A React component from The San Francisco Interface.

numbers.sfinterface.com

npm i @sfinterface/numbers
import { Numbers } from "@sfinterface/numbers";
import "@sfinterface/numbers/styles.css";

<Numbers value={count} />

Each column is a strip of digits behind a window. Changing the value turns only the columns whose digit actually moved, and the box grows or shrinks to fit what it now says.

Face, size, weight, colour and tracking are inherited. The stylesheet describes motion and geometry and nothing else, so a readout in a heading is the heading's type.

The digits are aria-hidden; one formatted string sits behind them, so a screen reader says one thousand two hundred and four, not twelve glyphs.

From The San Francisco Interface, shipped on its own.

Documentation

Every prop, every token and a playground to move them in: numbers.sfinterface.com

Formatting

Intl.NumberFormat. Currency, percentages, compact notation and every locale come from the platform.

<Numbers value={1125.64} format={{ style: "currency", currency: "USD" }} />
<Numbers value={0.0241}  format={{ style: "percent", maximumFractionDigits: 2 }} />
<Numbers value={48200}   format={{ notation: "compact" }} locale="en-GB" />

Transitions

<Numbers value={n} transition="tick" />

| | | | --- | --- | | roll | the strip turns, through every digit on the way | | tick | the old digit slides out, the new one slides in | | blur | it dissolves, defocused, in place | | flip | it turns over, like a split-flap board | | scale | it shrinks away and the new one grows |

Fade

fade is how far the soft edge of the window reaches. While a column turns it deepens, so the passing digits go ghostly and settle back.

<Numbers value={n} fade={0} />
<Numbers value={n} />
<Numbers value={n} fade={2.4} />

softness is the shape of that edge rather than its reach. At 0 a digit stays crisp almost to the edge and then lets go quickly; at 1 the same reach is spread into an even, eased ramp with no knee in it.

<Numbers value={n} softness={1} />
<Numbers value={n} fade={2} softness={0.6} />

Prefix and suffix

Anything set beside the number travels with it: the box is one inline box, so a symbol on either side stays against the digits as the number grows a column or loses one.

<Numbers value={1204} prefix="$" />
<Numbers value={12.4} suffix="%" />
<Numbers value={48} prefix={<Icon />} suffix="GB" />

--sfi-numbers-gap sets the air between an affix and the digits. It is 0em by default, which is what a currency symbol wants; a word wants about 0.25em.

Props

| prop | type | default | | --- | --- | --- | | value | number | required | | format | Intl.NumberFormatOptions | none | | locale | string \| string[] | the runtime's | | transition | "roll" \| "tick" \| "blur" \| "flip" \| "scale" | "roll" | | trend | "auto" \| "up" \| "down" | "auto" | | blur | boolean | true | | fade | number | 1 | | softness | number | 0 | | prefix | ReactNode | none | | suffix | ReactNode | none | | duration | number ms | 520 | | label | string | the formatted value |

Everything else is spread onto the root <span>.

Theming

Every value is a custom property. Set one on :root, on a wrapper, or inline.

| token | default | | | --- | --- | --- | | --sfi-numbers-roll | 520ms | how long a column takes to turn | | --sfi-numbers-exit | 240ms | how long a leaving column takes to go | | --sfi-numbers-cell | 1.4em | the pitch the digits are stacked at | | --sfi-numbers-blur | 0.09em | the deepest a fast column smears | | --sfi-numbers-fade | 1 | how far the window's soft edge reaches | | --sfi-numbers-fade-roll | 1.9 | how much further it reaches mid-turn | | --sfi-numbers-softness | 0 | how gradually that edge arrives, 0 crisp to 1 even | | --sfi-numbers-gap | 0em | the air between a prefix or suffix and the digits | | --sfi-numbers-ease | cubic-bezier(0.32, 0.72, 0, 1) | the curve a column turns on | | --sfi-numbers-ease-exit | cubic-bezier(0.4, 0, 1, 1) | and the one it leaves on |

:root {
  --sfi-numbers-roll: 700ms;
  --sfi-numbers-cell: 1.25em;
}

The stylesheet ships in one @layer arc, below your own rules, so a className of yours wins without !important.

Reduced motion

With prefers-reduced-motion: reduce the columns do not turn, blur or fade. The number changes, and nothing animates.

Requirements

React 18 or 19. No dependencies, types bundled. It renders on the server with the formatted value already in the markup, so there is no empty box and nothing to guard.

MIT