Stat
One number that matters, with what it measures and how it moved: the headline numbers of a dashboard, also called a KPI. Specification: docs/components/stat.md
Basics
A .canon-stat: a .canon-stat-label (what is measured, with its period), a .canon-stat-value (the number, formatted by your app), and optionally a .canon-stat-change and a .canon-stat-description. It is plain, with no frame of its own: the Card, the row or the page around it frames it.
Revenue this month
$48,210
Up 12% from last month
Goal: $50,000
The change
Your app writes the whole phrase, in the reader's language, with the direction in words: the arrow is decoration. data-direction (up, down, flat) draws the arrow; data-intent says whether the move is good (success), bad (danger), near a limit (warning) or worth noticing (info). The two are separate: an error rate going up is bad, a headcount going up is neither.
Revenue this month
$48,210
Up 12% from last month
Error rate
2.4%
Up 0.8 points since yesterday
Avg. response time
1.2 s
Down 0.3 s this week
Storage used
92%
Up 40%: near your plan's limit
Team members
38
3 more than last quarter
Open tickets
14
No change since Monday
A row of stats
.canon-stats lays stats side by side, as many columns as fit (each at least 12rem), wrapping onto more rows. Name it (role="group", aria-label) when the stats share a context the page doesn't give. data-lines draws a line between the stats of a row. Here in a Card, which frames them; drag the frame's corner to see them wrap.
This month
Revenue
$48,210
Up 12% from last month
New customers
126
Up 8 from last month
Churn
3.1%
Up 0.4 points
Overdue invoices
8
Same as last month
Sparkline
A .canon-stat-chart holds a sparkline your app draws: an inline <svg> with preserveAspectRatio="none" that strokes and fills with currentColor. Canon stretches it to the stat's width, keeps its lines their drawn width and colors it: quiet grey, or the change's color at 60%. It is aria-hidden="true" when the change already says the trend. Canon doesn't draw charts.
Signups, last 30 days
1,284
Up 18% from the 30 before
Failed payments
37
Up 9 from the 30 before
Active sessions
412
Peak at 14:00
Linking
A stat that leads to the list behind it: its label holds an <a class="canon-stat-link">, and the whole stat becomes the link, named by its label. One link per stat. Tab to see the focus ring around it.
Size
data-size on the stat, the row or a container: the value 24 / 30 / 36px, the rest to match. A headline stat is a bigger size beside smaller ones, not a different kind of stat.
sm
$48,210
Up 12% from last month
md
$48,210
Up 12% from last month
lg
$48,210
Up 12% from last month
Loading and empty
While the value loads: aria-busy="true" on the stat (or the row) and a Skeleton block in the value. No value: an empty .canon-stat-value shows a dash, which screen readers hear as "Not set". How fresh the number is goes in the description, in your words.
Revenue this month
Net promoter score
Not enough answers yet
Active users
2,931
Up 2% since yesterday
Updated 3 hours ago
Edge cases
A long label and change wrap; a value keeps its digits together and wraps only at spaces. Right to left, the stat starts on the right and the arrows mirror, as time runs the other way.
Average time from a ticket being opened to its first reply, business hours only
4 h 12 min
Down 38 minutes from the previous four weeks, across every queue
Annual recurring revenue
$12,480,915.50
الإيرادات هذا الشهر
٤٨٬٢١٠ $
ارتفاع ١٢٪ عن الشهر الماضي