Stat tiles
tier 2 — classes
One number that matters, with what it is and which way it moved.
.stat-row lays a set of them out without a breakpoint.
Every tone
Any tone tints a tile the same way, without moving its label or its figure.
<div class="stat-row">
<div class="stat stat--brand"><span class="stat-label">Launches this week</span><span class="stat-value">3</span></div>
<div class="stat stat--go"><span class="stat-label">Orders shipped</span><span class="stat-value">1,204</span></div>
<div class="stat stat--warn"><span class="stat-label">Hosting spend</span><span class="stat-value">€ 4,180</span></div>
<div class="stat stat--danger"><span class="stat-label">Failed exports</span><span class="stat-value">7</span></div>
<div class="stat stat--info"><span class="stat-label">Drafts open</span><span class="stat-value">12</span></div>
<div class="stat stat--secret"><span class="stat-label">Keys due to rotate</span><span class="stat-value">2</span></div>
</div>
A row of tiles
The parts: a label, a value in tabular-nums so a polling dashboard does not
reflow, and a .stat-delta saying which way it moved.
<div class="stat-row">
<div class="stat">
<span class="stat-label">Open orders</span>
<span class="stat-value">77</span>
<span class="stat-delta stat-delta--bad"><i class="ri-arrow-up-line"></i> <span>12 since 08:00</span></span>
</div>
<div class="stat">
<span class="stat-label">Resolved today</span>
<span class="stat-value">143</span>
<span class="stat-delta stat-delta--good"><i class="ri-arrow-up-line"></i> <span>18%</span></span>
</div>
<div class="stat">
<span class="stat-label">First response</span>
<span class="stat-value">4:12 <span class="stat-unit">median</span></span>
<span class="stat-delta stat-delta--warn"><i class="ri-arrow-up-line"></i> <span>0:38</span></span>
</div>
<div class="stat">
<span class="stat-label">Reservations accepted</span>
<span class="stat-value">86<span class="stat-unit">%</span></span>
<span class="stat-note">of 214 offered</span>
</div>
</div>
A wide value
.stat--lg steps the value up for a figure that is the point of the page, and .stat--sm steps the value down for a stat whose value is a timestamp, a range or
a name, so it fits its tile without leaving the row the reader compares it in. The label and
the unit keep their size, so the row still lines up.
<div class="stat-row stat-row--divided">
<span class="stat">
<span class="stat-label">Open</span>
<span class="stat-value">12</span>
</span>
<span class="stat">
<span class="stat-label">Failed today</span>
<span class="stat-value">0</span>
</span>
<span class="stat stat--sm">
<span class="stat-label">Last import</span>
<span class="stat-value">12.09.2026 02:30</span>
</span>
</div>
Target, link and unavailable
.stat--lg is the one figure a page leads with, at a larger value size.
.stat-target is the line saying what the number is measured against, and a bar
under it is a .progress. a.stat makes the
whole tile a link to the thing it counts; .stat--unavailable is for a figure
that could not be read — an em dash, never a zero.
<div class="sedna-col sedna-gap-3" style="max-width:560px">
<div class="stat stat--lg">
<span class="stat-label">Orders today</span>
<span class="stat-value">1 284</span>
<span class="stat-delta stat-delta--good"><i class="ri-arrow-up-line"></i> <span>9% on last Monday</span></span>
</div>
<div class="stat-row stat-row--divided">
<span class="stat">
<span class="stat-label">Dispatched</span>
<span class="stat-value">248</span>
<span class="stat-target">of a 300 target</span>
</span>
<a class="stat" href="#">
<span class="stat-label">Backordered</span>
<span class="stat-value">12</span>
<span class="stat-delta stat-delta--bad"><i class="ri-arrow-up-line"></i> <span>4 today</span></span>
</a>
<span class="stat stat--unavailable">
<span class="stat-label">Warehouse load</span>
<span class="stat-value">—</span>
<span class="stat-target">of a 300 target</span>
</span>
</div>
<div class="stat">
<span class="stat-label">Fulfilment rate</span>
<span class="stat-value">62<span class="stat-unit">%</span></span>
<span class="stat-target">62 of 80 lines reserved</span>
<span class="progress" style="margin-top:6px"><span class="progress-bar" style="width:62%"></span></span>
</div>
</div>
Over a threshold
.stat--warn and .stat--danger tint a tile whose figure has crossed
a limit — amber at a soft limit, red at or near a hard one — and colour its value.
The tile keeps its size, so its label and value still line up with the tiles beside it. Say
which limit in a .stat-target: the tint shows that a line was crossed, not which.
<div class="stat-row">
<div class="stat">
<span class="stat-label">Compute hours</span>
<span class="stat-value">1,284</span>
<span class="stat-target">of a 2,000 h allowance</span>
</div>
<div class="stat stat--warn">
<span class="stat-label">Hosting spend</span>
<span class="stat-value">€4,310</span>
<span class="stat-target">past the €4,000 soft limit</span>
</div>
<div class="stat stat--danger">
<span class="stat-label">Storage</span>
<span class="stat-value">96<span class="stat-unit">%</span></span>
<span class="stat-target">of the 2 TB hard limit</span>
</div>
<div class="stat">
<span class="stat-label">Days left in period</span>
<span class="stat-value">9</span>
<span class="stat-note">billing period ends 30 Nov</span>
</div>
</div>
Inside a card
A .stat-row directly inside a .card-body gets a dividing rule
between tiles. When the card is too narrow for one line the row wraps, and a tile that
starts a line has no rule in front of it. .stat-row--divided does the same
for a row straight on the page.
<div class="card" style="max-width:380px">
<div class="card-head"><strong>Last 24 hours</strong></div>
<div class="card-body">
<div class="stat-row">
<div class="stat">
<span class="stat-label">Received</span>
<span class="stat-value">312</span>
</div>
<div class="stat">
<span class="stat-label">Auto-handled</span>
<span class="stat-value">268</span>
</div>
<div class="stat">
<span class="stat-label">Escalated</span>
<span class="stat-value">44</span>
</div>
<div class="stat">
<span class="stat-label">Reopened</span>
<span class="stat-value">6</span>
</div>
</div>
</div>
</div>
Which way it moved
--good, --bad, --warn and the bare
.stat-delta. The modifier is judgement, not direction — more
open orders is up and bad, faster dispatch is down and good — so put a
ri-arrow-up-line or ri-arrow-down-line inside every delta to
carry the direction itself.
<div class="stat-row" style="max-width:640px">
<div class="stat">
<span class="stat-label">Dispatched</span>
<span class="stat-value">1,204</span>
<span class="stat-delta stat-delta--good"><i class="ri-arrow-up-line"></i> <span>8% on last week</span></span>
</div>
<div class="stat">
<span class="stat-label">Open orders</span>
<span class="stat-value">77</span>
<span class="stat-delta stat-delta--bad"><i class="ri-arrow-up-line"></i> <span>12 since 08:00</span></span>
</div>
<div class="stat">
<span class="stat-label">Time to dispatch</span>
<span class="stat-value">4.2<span class="stat-unit">h</span></span>
<span class="stat-delta stat-delta--good"><i class="ri-arrow-down-line"></i> <span>20 min faster</span></span>
</div>
<div class="stat">
<span class="stat-label">Returns</span>
<span class="stat-value">9</span>
<span class="stat-delta stat-delta--warn"><i class="ri-arrow-up-line"></i> <span>2 — watching</span></span>
</div>
<div class="stat">
<span class="stat-label">Regions live</span>
<span class="stat-value">3</span>
<span class="stat-delta"><i class="ri-subtract-line"></i> <span>unchanged</span></span>
</div>
</div>