Skip to content

Components · Data display

StatCard

A KPI tile: label, formatted value, change versus a period, hint, help, sparkline and drill-down.

@rookcom/monarch-reactComponent

KPI row with changes

Revenue

$1,284,500.00

Cost per lead

$41.20

Reply rate

8.1%

212 of 2,617 contacts

Usage examples

Help, unit, sparkline and drill-down

Sizes and plain variant

Loading, empty and error

0.3 delta props

Purpose

A single key figure for a dashboard or overview, with how it changed, what it is compared with, and where to go for detail. StatCardGroup lays several out as a responsive KPI row.

Use when

  • Headline metrics on a home, overview or reports page.
  • A metric that links to the filtered list behind it ("7 overdue invoices").
  • Metrics whose definition needs explaining ("How first response is calculated").

Avoid when

  • A trend over time is the point: use a line or bar chart.
  • Grouping several facts about one record: use DescriptionList.
  • A share of a whole: use StackedProportionBar or a donut.

Design principles

  • The product decides what the number is; StatCard only formats it (format, Intl.NumberFormat, Money for currency) and never invents a change.
  • Every state is honest: loading shows a placeholder, a missing value shows a dash read as "No data", a failure shows the error with an optional Try again.
  • Direction is spoken, not only drawn: both the legacy delta and change (DeltaIndicator) announce the direction in words.
  • change knows which direction is better (goodDirection), so falling costs are green.
  • A linked card has one link, named by its label; the whole card is the click target.
  • Additive upgrade: the 0.3 props (delta, deltaDirection, deltaTone, caption, visual) render exactly as before.

API reference

import { StatCard, StatCardGroup } from '@rookcom/monarch-react';
ExportKindRenderingRoot class
StatCardcomponentserver-safe.mn-stat
StatCardGroupcomponentserver-safe.mn-stat

All native <div> attributes and the ref go to the root .mn-stat.

StatCardGroup is a grid <div>; with label it is a named role="group".

change takes DeltaIndicator props (from @rookcom/monarch-react/charts) without importing that entry.

Generated from the TypeScript declarations of packages/react/src/components/data-display/stat-card.

Properties

With format="percent" the value is a ratio: 0.12 is 12%. Without format, a number is shown as given (0.3 behaviour).

If both delta and change are set, change wins.

StatCard

A single KPI for dashboards: label, value, change, comparison period and optional hint, help, sparkline and drill-down link. Group several with StatCardGroup.

PropTypeDescription
label requiredReactNode
value requiredReactNodeThe value. Pre-formatted text ("₹12.4L", "143"), or a number formatted by `format`. `null` or `undefined` shows the empty state.
captionReactNodePeriod label, e.g. "vs last 30 days". With `change`, it becomes the change's spoken comparison.
changeOmit<DeltaIndicatorProps, "ref">A DeltaIndicator in the footer, e.g. `{ value: 0.12 }` or `{ current, previous, goodDirection: 'down' }`. Replaces `delta`; `caption` is used as its comparison unless it has its own.
childrenReactNode
classNamestring
currencystringISO 4217 code for `format="currency"`.
deltaReactNodeChange versus the previous period, e.g. "+12%". For a computed, polarity-aware change use `change`.
deltaDirection"flat" | "up" | "down"Direction of the change; `up` is success unless `deltaTone` says otherwise.
deltaTone"danger" | "success" | "neutral"Override the colour of the delta, e.g. `danger` when up is bad (churn).
emptyLabelstringSpoken for a missing value, shown as a dash. Default "No data".
errorReactNodeReplaces the value with this error message.
format"number" | "currency" | "percent"Formats a numeric `value` with `Intl.NumberFormat`: `number`, `percent` (a ratio: 0.12 is 12%) or `currency` (through Money, needs `currency`). Without it a number is shown as given.
formatOptionsIntl.NumberFormatOptionsExtra `Intl.NumberFormat` options for `format`, e.g. `{ maximumFractionDigits: 1 }`.
helpReactNodeHow the metric is defined, shown in a popover from an info button next to the label, e.g. "Median time from ticket creation to first agent reply."
helpLabelstringAccessible name of the help button. Default "About {label}" when `label` is text, else "About this metric".
hintReactNodeA sub-line under the value, e.g. "38 of 52 people" or "Unique: 1,204".
hrefstringDrill-down: the label becomes a link and the whole card is its click target.
iconReactNodeDecorative icon before the label.
idstring
linkComponentElementTypeComponent used for `href`, e.g. a router link. Default `a`.
loadingbooleanShows a placeholder for the value and sets `aria-busy`.
localestringBCP 47 locale for `format`. Default: the runtime locale.
notation"compact" | "standard"`compact` writes 1.2K or $1.2M and gives the full value to assistive technology. Default `standard`.
onRetry(() => unknown)With `error`, shows Try again (InlineErrorRetry) calling this.
semantics"text" | "description-list"`text` (default) renders paragraphs. `description-list` renders the label as `<dt>` and the value lines as `<dd>` inside a `<dl>`.
size"sm" | "md" | "lg"`sm` is compact (dense rows), `md` (default), `lg` is a hero figure.
sparklineReactNodeA trend line under the value (for example a Sparkline). Decorative: the value and change carry the data.
tone"danger" | "success" | "warning" | "neutral" | "accent"Colour role for thresholds or emphasis. `accent` highlights the card; `success`, `warning` and `danger` colour the value. Pair it with words in `hint`: colour is not announced.
unitReactNodeUnit shown smaller after the value, e.g. `ms` or `GB`.
variant"card" | "plain"`card` (default) has a border and surface; `plain` has neither, for use inside another surface.
visualReactNodeSparkline or icon slot.

StatCardGroup

A responsive grid of StatCards (a KPI row).

PropTypeDescription
childrenReactNode
classNamestring
columnsnumberColumns on wide screens (from 48rem). Narrower screens use two columns, then one. Default: as many as fit at `minItemWidth`.
idstring
labelstringNames the group (`role="group"`), e.g. "This month".
minItemWidthstringNarrowest a card gets in the automatic layout. Default `12rem`.

Variants

PropValuesDescription
size'sm''md''lg'sm is compact for dense rows, md the default, lg a hero figure (data-size).
variant'card''plain'plain drops the border, padding and surface for use inside another card (data-variant).
tone'neutral''accent''success''warning''danger'accent tints the card; status tones colour the value and icon for thresholds (data-tone).
format'number''percent''currency'Formats a numeric value with locale, notation, formatOptions and currency.
semantics'text''description-list'description-list renders <dl> with the label as <dt> and value lines as <dd>.
deltaDirection / deltaTone'up''down''flat'The 0.3 pre-formatted delta, still supported.

States

StateExposed asBehaviour
Loadingdata-state="loading"A placeholder replaces the value; the footer, hint and sparkline are hidden; aria-busy="true".
Emptydata-state="empty"value null or undefined: a dash read as emptyLabel ("No data").
Errordata-state="error"error replaces the value; with onRetry it is an InlineErrorRetry with Try again.
Linkeddata-linkedhref makes the label a link stretched over the card.
With change–A DeltaIndicator (or the 0.3 delta) and the comparison period in the footer.

Interaction behaviour

  • With href, clicking anywhere on the card follows the link; hover strengthens the border and underlines the label.
  • With help, the info button opens a popover with the definition; it closes on Escape or outside click and returns focus to the button.
  • Otherwise StatCard is read-only.

Keyboard support

KeysAction
TabMoves to the label link (with href), then the help button (with help).
EnterFollows the link, or opens the help popover.
SpaceOpens the help popover.
EscapeCloses the help popover and returns focus to its button.

A card without href, help or onRetry has no focusable parts.

Accessibility

Role
none by default; semantics="description-list" gives a <dl> term and descriptions
Accessible name
The label; a linked card’s link is named by the label alone, not the whole card.
  • Change direction is spoken ("up 12% vs last month, better"); arrows are aria-hidden.
  • Compact values ("1.3M") are read in full ("1,250,000").
  • The help button is named "About {label}" (or helpLabel) and exposes aria-expanded.
  • Icons, visual and sparkline are aria-hidden: never put the only copy of data there.
  • Tone is colour; say what it means in hint ("Above the 5% target").
  • Loading sets aria-busy; the placeholder is hidden from assistive technology.
  • The linked card shows a focus ring around the whole card when its link has keyboard focus.

Responsive behaviour

  • A card fills its grid cell; long labels wrap and the visual slot does not shrink.
  • StatCardGroup fits as many cards as minItemWidth (12rem) allows, down to one column at 320px.
  • With columns, it shows one column below 30rem, at most two up to 48rem, then columns.

Theme support

  • Surface, border and text use semantic tokens; status tones use the -strong text tokens, which meet AA in light, dark and high-contrast themes.
  • accent uses --mn-color-accent-subtle and --mn-color-accent-border.
  • The sparkline slot is coloured --mn-color-data-1 (series palette), never a status colour.

Do and don't

Do

  • Pass raw numbers with format and the viewer’s locale, or a value the product has already formatted.
  • Set goodDirection: "down" in change for costs, latency and churn.
  • Always pair a change with its comparison period (caption).
  • Pass null when there is no value yet, and error when it failed to load.

Don't

  • Do not show a placeholder number (0, 0 MB) when the data is not available.
  • Do not put the only copy of information in visual or sparkline; they are hidden from assistive technology.
  • Do not use tone as the only way to show a threshold was crossed.
  • Do not nest other links or buttons in a linked card apart from help.
  • DeltaIndicator: The change on its own, in tables and rows.
  • Money: How currency values are formatted.
  • Sparkline: The trend line for the sparkline slot (@rookcom/monarch-react/charts).
  • DescriptionList: Several facts about one record.
  • Card: General content surface.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton, Money, DeltaIndicator, InlineErrorRetry, Skeleton, VisuallyHidden, Popover

Testing coverage

  • Reads the 0.3 delta direction through visually hidden text and applies deltaTone; legacy markup unchanged.
  • Formats number, percent, currency (Money) and compact values with the full value spoken.
  • Unit and hint; polarity-aware change with the caption as its comparison.
  • Help popover opens with Enter, closes with Escape and returns focus; custom help label.
  • Drill-down link named by the label, reachable with Tab, custom link component.
  • Loading (aria-busy), empty ("No data") and error with Try again.
  • description-list semantics, tone/size/variant attributes, ref forwarding.
  • StatCardGroup group role, column variables and minimum width.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/stat-card; Cross-browser axe: /components/stat-card

Tests: packages/react/src/components/data-display/stat-card