Progress and spinners
tier 2 — classesThree ways to say “working”, picked by how much you know about what is coming. The fourth — the shape of what is coming, before it arrives — is a skeleton.
.progress bar. No
percentage, but a known shape — a table of rows, a card — is a
.skeleton. Neither is a .spinner. Nothing at all yet, in a panel that will fill, is
.empty-state--pending.
Progress bar
Determinate: set the inline width on .progress-bar.
Indeterminate: leave the width off and add .progress--indeterminate to the
track. Put the number in text as well — a bar is a shape, and a reader deciding whether to
wait needs the figure.
Uploading — 72%
Complete
Slower than expected — still running
Stopped at 34% — quota exceeded
Working — no total known
<div style="display:flex; flex-direction:column; gap:14px; max-width:420px">
<div>
<div class="progress"><div class="progress-bar" style="width:72%"></div></div>
<p class="form-hint">Uploading — 72%</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--go" style="width:100%"></div></div>
<p class="form-hint">Complete</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--warn" style="width:72%"></div></div>
<p class="form-hint">Slower than expected — still running</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--danger" style="width:34%"></div></div>
<p class="form-hint">Stopped at 34% — quota exceeded</p>
</div>
<div>
<div class="progress progress--indeterminate"><div class="progress-bar"></div></div>
<p class="form-hint">Working — no total known</p>
</div>
</div>
Measuring towards a threshold
.progress-bar--muted is for a bar measuring progress towards a
threshold rather than the completion of a task, where well below the line is neither a
warning nor a failure. The base bar is var(--brand), which reads as an alarm
wherever the brand is a red.
Password reset — 94% sent unchanged, past the 90% threshold
Group membership — 86%, close enough to watch
Licence assignment — 31%, nowhere near it yet
<div style="display:flex; flex-direction:column; gap:14px; max-width:420px">
<div>
<div class="progress"><div class="progress-bar progress-bar--go" style="width:94%"></div></div>
<p class="form-hint">Password reset — 94% sent unchanged, past the 90% threshold</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--warn" style="width:86%"></div></div>
<p class="form-hint">Group membership — 86%, close enough to watch</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--muted" style="width:31%"></div></div>
<p class="form-hint">Licence assignment — 31%, nowhere near it yet</p>
</div>
</div>
Ranked bars
.progress-bar--sev-1 … --sev-5 take the ordered ramp behind the
severity badges, for a bar that measures a rank rather than a fraction.
They are a fill and carry no label — where there is one, use .badge-sev-N.
P1 — 4 of 51 open incidents
P2 — 11 of 51
P3 — 23 of 51
P4 — 9 of 51
P5 — 4 of 51
<div style="display:flex; flex-direction:column; gap:10px; max-width:420px">
<div>
<div class="progress"><div class="progress-bar progress-bar--sev-1" style="width:8%"></div></div>
<p class="form-hint">P1 — 4 of 51 open incidents</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--sev-2" style="width:22%"></div></div>
<p class="form-hint">P2 — 11 of 51</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--sev-3" style="width:45%"></div></div>
<p class="form-hint">P3 — 23 of 51</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--sev-4" style="width:18%"></div></div>
<p class="form-hint">P4 — 9 of 51</p>
</div>
<div>
<div class="progress"><div class="progress-bar progress-bar--sev-5" style="width:8%"></div></div>
<p class="form-hint">P5 — 4 of 51</p>
</div>
</div>
Spinner
Sized in em, so it matches whatever text or button it sits in without a second
class. .spinner-lg is the standalone size for a panel that is still loading.
<span>Checking the connection <span class="spinner"></span></span>
<button class="btn" disabled><span class="spinner"></span> Sending…</button>
<button class="btn btn-primary" disabled><span class="spinner"></span> Saving</button>
<span class="spinner spinner-lg"></span>
A list of running work
Where these end up in practice: one row per job, with the shape that matches what is known —
a percentage gets a determinate bar, a wait with no total gets the indeterminate one and a
.spinner, and anything finished gets a badge instead. Add
aria-busy="true" to the row while it is running, and put the figure in text as
well as in the bar.
- build 4182 — orders-api Compiling · started 2 min ago
- deploy 4181 — EU-West Waiting for the health check — no total known
- deploy 4180 — EU-Central Rolled back at 34% — quota exceeded
- deploy 4179 — UK-South Live since 08:41
<ul class="list" style="max-width:520px">
<li>
<div class="list-row">
<i class="ri-git-commit-line"></i>
<span class="list-main">
<span class="list-title">build 4182 — orders-api</span>
<span class="list-sub">Compiling · started 2 min ago</span>
<span class="progress sedna-mt-1"><span class="progress-bar" style="width:64%"></span></span>
</span>
<span class="list-meta text-nums">64%</span>
</div>
</li>
<li>
<div class="list-row">
<i class="ri-rocket-line"></i>
<span class="list-main">
<span class="list-title">deploy 4181 — EU-West</span>
<span class="list-sub">Waiting for the health check — no total known</span>
<span class="progress progress--indeterminate sedna-mt-1"><span class="progress-bar"></span></span>
</span>
<span class="list-meta"><span class="spinner"></span></span>
</div>
</li>
<li>
<div class="list-row">
<i class="ri-close-circle-line"></i>
<span class="list-main">
<span class="list-title">deploy 4180 — EU-Central</span>
<span class="list-sub">Rolled back at 34% — quota exceeded</span>
<span class="progress sedna-mt-1"><span class="progress-bar progress-bar--danger" style="width:34%"></span></span>
</span>
<span class="list-meta"><span class="badge badge-danger">Failed</span></span>
</div>
</li>
<li>
<div class="list-row">
<i class="ri-check-double-line"></i>
<span class="list-main">
<span class="list-title">deploy 4179 — UK-South</span>
<span class="list-sub">Live since 08:41</span>
<span class="progress sedna-mt-1"><span class="progress-bar progress-bar--go" style="width:100%"></span></span>
</span>
<span class="list-meta"><span class="badge badge-go">Done</span></span>
</div>
</li>
</ul>