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.
DR.Simple_UI
Showing main. This can be ahead of the version your app has installed — check what a class says it needs before copying it. Releases (opens in a new tab)

Progress and spinners

tier 2 — classes

Four ways to say “working”. Which one depends on whether you know how far along it is, and whether you know the shape of what is coming.

Pick by what you know. A percentage is a .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 — the one value only your app knows. 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 who wants to know whether to wait needs the figure. Under prefers-reduced-motion the indeterminate bar is filled rather than blanked, so it still says “in progress, no estimate”.

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>

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.

A spinner says only that something is happening. It stays visible under prefers-reduced-motion — static, but still reading as busy.

Checking the connection
<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>

Skeleton

Give each placeholder the height and width of the content that is coming, so nothing reflows when it arrives. Vary the line widths: identical bars read as a table rather than a paragraph.

Mark the container aria-busy="true". The bars themselves are decoration and should not be announced one by one.

<div class="card" style="max-width:420px">
    <div class="card-head">
        <div class="skeleton skeleton-avatar"></div>
        <div class="skeleton skeleton-title" style="width:40%"></div>
    </div>
    <div class="card-body">
        <div class="skeleton skeleton-text"></div>
        <div class="skeleton skeleton-text" style="width:88%"></div>
        <div class="skeleton skeleton-text" style="width:64%"></div>
    </div>
</div>

A list of running work

Where these three 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 — a bar at 100% for good says “still working”.

Add aria-busy="true" to the row while it is running, and put the figure in text as well as in the bar. A bar's width is not a number to anybody who cannot see it.

  • build 4182 — orders-api Compiling · started 2 min ago 64%
  • deploy 4181 — EU-West Waiting for the health check — no total known
  • deploy 4180 — EU-Central Rolled back at 34% — quota exceeded Failed
  • deploy 4179 — UK-South Live since 08:41 Done
<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 &mdash; orders-api</span>
                <span class="list-sub">Compiling · started 2 min ago</span>
                <span class="progress dr-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 &mdash; EU-West</span>
                <span class="list-sub">Waiting for the health check &mdash; no total known</span>
                <span class="progress progress--indeterminate dr-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 &mdash; EU-Central</span>
                <span class="list-sub">Rolled back at 34% &mdash; quota exceeded</span>
                <span class="progress dr-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 &mdash; UK-South</span>
                <span class="list-sub">Live since 08:41</span>
                <span class="progress dr-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>