Skip to content

Components / Sections

Stats Row

Shows big numbers that count up when scrolled into view, 120 ms apart, with labels and an optional bar under each, in a hairline row. No script of its own: CountUp and Progress do the counting.

Live demo

102

primitives

67

at zero bytes

1.9

kB raw: the runtime, once per site

Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.

Variants · each row writes the attribute the prop writes; the section's own CSS answers

tone
density

Measured

JavaScript of its own

0 B

Uses Reveal and the shared runtime (1.9 kB raw, once per site). With those included: 3.0 kB raw.

CSS 12.4 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · newly available

Chrome 91 · Firefox 128 · Safari 17; needs property, counter-style. Elsewhere: the final numbers, shown at once.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/stats-row
src/pages/index.astro
---
import StatsRow from '@/components/moonarc/pro/StatsRow.astro';
---
<StatsRow stats={[
  { value: 1240, label: 'teams on the platform' },
  { value: 38, label: 'regions live', of: 40 },
  { value: 4.9, label: 'average rating', decimals: 1 },
  { value: 12, label: 'years in business' },
]} />

Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: count-up, progress.

Props

PropTypeDefaultDescription
stats{ value, label, prefix?, suffix?, decimals?, of? }[]noneNumbers; `of` adds a bar showing value / of.
tone'surface' | 'muted' | 'ink' | 'brand'unset (paints nothing)The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel. Each paints --background, --ma-tone-muted (over --muted, with a --border hairline), --ma-tone-ink or --ma-tone-brand (over --primary) with the matching text. With theme.css, a painted tone also re-declares --card, --border, --input and --ring inside the section, so cards and fields follow the ground. Decoration is strongest on brand.
density'compact' | 'cozy' | 'roomy''cozy'Cell padding ×0.8 / ×1 / ×1.25 through --ma-density.

Theming and overrides

The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.

Classes

.ma-stats.ma-stats__bar.ma-stats__cell.ma-stats__copy.ma-stats__eyebrow.ma-stats__head.ma-stats__headline.ma-stats__label.ma-stats__value

Slots

slot="eyebrow"slot="headline"slot="copy"

Reduced motion

Final numbers and full bars, shown at once.

With ClientRouter

Inherits CountUp and Progress; nothing to wire.

Why it is built this way

Replaces: Stats sections (Tailwind Plus) · NumberTicker rows (Magic UI). See the migration table.

Counts a number up from zero to its value when it scrolls into view, with thousands separators, decimals, prefix and suffix.

newly · Chrome 91 · Firefox 128 · Safari 17scroll

Progress

Loading

A bar that fills to its value when it scrolls into view, with the number counting up beside it, or a ring with the number in the middle.

newly · Chrome 91 · Firefox 128 · Safari 17scroll

Lays out features as a bento grid: one slab split by 1 px hairlines, with the page ground showing through the gaps.

every browserscroll

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown