Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.31.0 · main

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.

The label goes above the value So the reader never holds a number in their head while they find out what it means.

Every tone

Any tone tints a tile the same way, without moving its label or its figure.

Launches this week3
Orders shipped1,204
Hosting spend€ 4,180
Failed exports7
Drafts open12
Keys due to rotate2
<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.

Open orders 77 12 since 08:00
Resolved today 143 18%
First response 4:12 median 0:38
Reservations accepted 86% of 214 offered
<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.

Open 12 Failed today 0 Last import 12.09.2026 02:30
<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>

.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.

Orders today 1 284 9% on last Monday
Dispatched 248 of a 300 target Backordered 12 4 today Warehouse load — of a 300 target
Fulfilment rate 62% 62 of 80 lines reserved
<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">&mdash;</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.

Compute hours 1,284 of a 2,000 h allowance
Hosting spend €4,310 past the €4,000 soft limit
Storage 96% of the 2 TB hard limit
Days left in period 9 billing period ends 30 Nov
<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">&euro;4,310</span>
        <span class="stat-target">past the &euro;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.

Last 24 hours
Received 312
Auto-handled 268
Escalated 44
Reopened 6
<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.

Dispatched 1,204 8% on last week
Open orders 77 12 since 08:00
Time to dispatch 4.2h 20 min faster
Returns 9 2 — watching
Regions live 3 unchanged
<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 &mdash; 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>