Stat
Stat — the KPI tile: a figure, what it measures, and how it moved.
Examples
- 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.
- 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.
| Token | Default |
|---|---|
| --stat-change-color | var(--stat-positive-color, var(--color-success)) |
| --stat-change-font-size | var(--font-size-sm) |
| --stat-change-font-weight | var(--font-weight-medium) |
| --stat-change-gap | var(--space-1) |
| --stat-change-marker | "\2191" |
| --stat-change-numeric | normal |
| --stat-change-row-gap | var(--space-2) |
| --stat-group-gap | var(--space-6) |
| --stat-group-min | 10rem |
| --stat-period-color | var(--color-on-surface-muted) |
| --stat-row-gap | var(--space-1) |
| --stat-value-color | var(--color-on-surface) |
| --stat-value-font-family | var(--font-primary) |
| --stat-value-font-size | var(--font-size-2xl) |
| --stat-value-font-weight | var(--font-weight-semibold) |
| --stat-value-letter-spacing | var(--letter-spacing-tight) |
| --stat-value-numeric | slashed-zero |
| --stat-negative-color | var(--color-danger) |
| --stat-neutral-color | var(--color-on-surface-muted) |
| --stat-positive-color | var(--color-success) |
| --stat-label-color | var(--color-on-surface-muted) |
| --stat-label-font-family | var(--font-secondary) |
| --stat-label-font-size | var(--font-size-sm) |
| --stat-label-font-weight | var(--font-weight-medium) |
| --stat-label-letter-spacing | var(--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
- The direction needs words.
.stat-upand.stat-downsupply 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-successand 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