Skip to content
Component installs need the registry setup
VegaStack Design

Stat

A labelled value block — muted label over a value, an honest muted empty state, and an optional delta line. Two scales.

Status
stable
Since
0.3.0
Accessibility pattern
label and value pair

Last updated

Estimated ARR
$1M–$10M
Employee range
11–50
Connection strength
No connection

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Stat before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify stat

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/stat

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

The same command installs the registry items it composes: @vegastack/skeleton.

Usage

import {
  Stat,
  StatDelta,
  StatEmpty,
  StatLabel,
  StatValue,
} from "@/components/ui/stat";

<Stat>
  <StatLabel>Estimated ARR</StatLabel>
  <StatValue>$1M–$10M</StatValue>
</Stat>;

Dashboard tiles

size="lg" scales the value to text-2xl (24px, tabular) for stat tiles; add a StatDelta with intent="up" | "down" | "flat" — the sign/arrow in the copy carries the meaning, color is never the only signal.

Active companies
1,284
↑ 12% this month
Churned
17
↓ 3 vs last month
Open deals
62
No change

Honest empties

When there is no value, render StatEmpty ("No connection", "No team") in the text-muted-foreground placeholder register — never a dash or a zero that would read as data. This mirrors the system-wide inline-empty tier (a muted placeholder span).

Anatomy

Stat is a compound component. Every exported part, with the data-slot it renders (generated from the canonical source):

Stat — data-slot="stat"
StatDelta — data-slot="stat-delta"
StatEmpty — data-slot="stat-empty"
StatLabel — data-slot="stat-label"
StatSkeleton — data-slot="stat-skeleton" | "stat-skeleton-label" | "stat-skeleton-value"
StatValue — data-slot="stat-value"
StatGroup — data-slot="stat-group" | "stat-group-grid" | "stat-group-item"

Examples

The default scale suits record highlights; the tile preview exercises the large value scale and all three delta intents.

Estimated ARR
$1M–$10M
Employee range
11–50
Connection strength
No connection
Active companies
1,284
↑ 12% this month
Churned
17
↓ 3 vs last month
Open deals
62
No change

Linked stat tiles

When a count opens the list it counts, make the whole tile the link: an outline Item rendered as an <a>, with the label first and the value after it. The link's accessible name is the tile's text in reading order, so each tile announces as "Overdue tasks 3" with no aria-label to keep in sync. While counts load, a Skeleton stands in for the value and the label keeps naming the link.

Stat group

StatGroup is a compact row of inline stats — "Overdue 1 · Today 3 · Upcoming 12" — in one hairline-divided strip, for a home page or a dashboard header where four tiles would take a whole row. It follows its own width, not the viewport: one row of equal cells from 24rem, a 2×2 grid below. A cell with an href is a link through linkRender (your router's Link), named label first. undefined is loading (a skeleton holds the value's line), null is a failed read ("—" and errorLabel, never 0), and hideWhenAllZero renders nothing when every value is exactly zero.

Drafts
475
Missing specs
122
Published
Archived
Couldn't load

Loading

StatSkeleton is a Stat while it loads — a label bar over a value bar at the Stat's own size — for a whole band of tiles before any count arrives.

API Reference

Stat

PropTypeDefaultDescription
size"lg" | "md"'md'Scale — default for facts/highlights rows, lg for dashboard tiles.

Data attributes and CSS variables on Stat

AttributeValues
data-sizemirrors a prop or state value
data-slot"stat"

StatLabel, StatValue, and StatEmpty

These structural parts accept native <div> props and forward their refs to their respective elements.

StatDelta

PropTypeDefaultDescription
intent"down" | "flat" | "up"'flat'Direction of the change — up reads success, down reads destructive, flat stays muted. Pair the copy with a sign/arrow; color is never the only signal.

Data attributes and CSS variables on StatDelta

AttributeValues
data-intentmirrors a prop or state value
data-slot"stat-delta"

StatSkeleton

PropTypeDefaultDescription
classNamestring—Classes for the block.
size"lg" | "md""md"The scale of the Stat it stands in for.

Data attributes and CSS variables on StatSkeleton

AttributeValues
data-sizemirrors a prop or state value
data-slot"stat-skeleton" | "stat-skeleton-label" | "stat-skeleton-value"

StatGroup

PropTypeDefaultDescription
items*readonly StatGroupItem[]—The stats, in order — usually two to four.
errorLabelstring"Couldn't load"Shown under the dash when a value failed to load.
formatValue((value: number) => string)(n) => n.toLocaleString()Formats a value.
hideWhenAllZerobooleanfalseRender nothing when every value is exactly 0 (loading and failed values are not zero), so an all-quiet row takes no space.
linkRenderReact.ReactElement<unknown, string | React.JSXElementConstructor<any>><a />The element a linked cell renders as — your router's link (<Link />). The cell's href, classes and children are merged into it.
size"lg" | "md""lg"Scale of each stat's value.

Data attributes and CSS variables on StatGroup

AttributeValues
data-slot"stat-group" | "stat-group-grid" | "stat-group-item"

Each item:

PropTypeDefaultDescription
label*string—The label, read before the value ("Overdue 3").
value*number | null | undefined—The count: undefined while it loads, null when the read failed (never shown as 0).
hrefstring—The list the stat counts — the cell becomes a link through linkRender.
idstring—Stable key; the label is used when omitted.

Accessibility

  • Keep the label visible so the value has an adjacent human-readable name.
  • Delta copy carries direction with words or a sign; color is supplementary.
  • Use StatEmpty for missing data instead of a dash or a misleading numeric zero.
  • Tabular numerals keep changing values aligned without changing their spoken representation.
  • A StatGroup cell that is loading is aria-busy; its dash on a failed read is hidden from assistive technology, which hears errorLabel instead.
ContractStates tested
Behaviourdefault, empty, error, success, group, group-link, group-loading, group-failed, group-hidden-all-zero
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test
Visualdefault, error, success, empty

Do / Don't

Do
Pair every value with a concise label and write delta text that remains meaningful without color.
Don't
Animate rapidly changing metrics in a live region or substitute zero for unknown data.

On this page