Stat

dl.stat

Stat — the KPI tile: a figure, what it measures, and how it moved.

Examples

A figure with its change

View “A figure with its change” as
Revenue
1,284,309
Increased by+12.4% vs last month
<dl class="stat">
  <dt class="stat-label">Revenue</dt>
  <dd class="stat-value">1,284,309</dd>
  <dd class="stat-change stat-up">
    <span class="sr-only">Increased by</span>+12.4%
    <span class="stat-period">vs last month</span>
  </dd>
</dl>

A <dl>, because a label and its value is exactly what one is for.

The sign carries the direction — it survives copy-paste and reads on a braille display — and the arrow and the colour are redundancy on top of it, which is the right order of reliance.

When up is bad

View “When up is bad” as
Churn
4.1%
Increased by+0.6pp vs last month
<dl class="stat" style="--stat-positive-color: var(--color-danger); --stat-negative-color: var(--color-success)">
  <dt class="stat-label">Churn</dt>
  <dd class="stat-value">4.1%</dd>
  <dd class="stat-change stat-up">
    <span class="sr-only">Increased by</span>+0.6pp
    <span class="stat-period">vs last month</span>
  </dd>
</dl>

Churn, latency, cost and refund rate all go the other way. Swap the two colour tokens on the stat rather than misusing the direction classes, whose job is the arrow.

Tokens 25

Level 2, declared on .stat itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by stat
TokenDefault
--stat-change-colorvar(--stat-positive-color, var(--color-success))
--stat-change-font-sizevar(--font-size-sm)
--stat-change-font-weightvar(--font-weight-medium)
--stat-change-gapvar(--space-1)
--stat-change-marker"\2191"
--stat-change-numericnormal
--stat-change-row-gapvar(--space-2)
--stat-group-gapvar(--space-6)
--stat-group-min10rem
--stat-period-colorvar(--color-on-surface-muted)
--stat-row-gapvar(--space-1)
--stat-value-colorvar(--color-on-surface)
--stat-value-font-familyvar(--font-primary)
--stat-value-font-sizevar(--font-size-2xl)
--stat-value-font-weightvar(--font-weight-semibold)
--stat-value-letter-spacingvar(--letter-spacing-tight)
--stat-value-numericslashed-zero
--stat-negative-colorvar(--color-danger)
--stat-neutral-colorvar(--color-on-surface-muted)
--stat-positive-colorvar(--color-success)
--stat-label-colorvar(--color-on-surface-muted)
--stat-label-font-familyvar(--font-secondary)
--stat-label-font-sizevar(--font-size-sm)
--stat-label-font-weightvar(--font-weight-medium)
--stat-label-letter-spacingvar(--letter-spacing-normal)

Variants and states

Variants

  • .stat-down
  • .stat-flat
  • .stat-lg
  • .stat-sm

Inside it

  • .stat-change
  • .stat-label-first
  • .stat-label
  • .stat-value
  • .stat-group
  • .stat-period
  • .stat-up

State it reads

Read from the platform, never mirrored into a class that could disagree with it.

  • data-legacy

Before you ship it

What you have to do 1 requirements

  1. The direction needs words. .stat-up and .stat-down supply an arrow and a colour, and neither is text: colour alone excludes a large fraction of readers, and the arrow is drawn as decorative generated content. Put the direction in the content itself — a literal + or in the figure, and a <span class="sr-only">Increased by</span> before it: <dl class="stat"> <dt class="stat-label">Revenue</dt> <dd class="stat-value">1,284,309</dd> <dd class="stat-change stat-up"> <span class="sr-only">Increased by</span>+12.4% <span class="stat-period">vs last month</span> </dd> </dl> The sign survives copy-paste and reads in a braille display; the arrow and the colour are redundancy on top of it, which is the correct order of reliance. Up is not always good. The default pairs up with --color-success and down with --color-danger, which is wrong for churn, latency, cost and refund rate. Swap the two on the stat rather than misusing the direction classes, whose job is the arrow: <dl class="stat" style="--stat-positive-color: var(--color-danger); --stat-negative-color: var(--color-success)">

src/css/components/stat.css · npx mostlycss add stat