Loaders & states
A system is judged on its loading states, not its buttons.
Loading is when the user is least patient and most likely to leave. Empty and broken are the screens nobody designs and everybody sees.
Which loader, and when
Match the shape of the wait, not the taste of the designer.
| Duration | Use | Why |
|---|---|---|
| < 300ms | Nothing at all | A spinner that flashes for 80ms reads as a glitch and makes a fast app feel broken. |
| 0.3 – 1s | Inline — the control shows it is busy | The user is still looking at the thing they clicked. |
| 1 – 5s | Skeleton shaped like the content | Holds the layout so nothing jumps when data lands. |
| > 5s | Determinate progress, with a number | Past five seconds people need to know it is finite. |
| Unknown | Indeterminate bar at the top | Never block the screen for a wait you cannot measure. |
The sweeping-gradient shimmer everyone ships is a gradient (forbidden by this language), it moves for decoration rather than information, and it draws the eye to the emptiest part of the screen. A calm opacity pulse says the same thing, quietly.
See the difference
<Delayed> · <SkeletonTable> · <Busy>Run the same fetch three ways. The skeleton is the only one where nothing moves when the data arrives.
The set
spinner — three sizes, never alone on a page
inline-loader — busy, in place
skeleton-text — the last line is short, like real prose
skeleton — text, title, circle, pill, block
loading-bar — indeterminate, never blocking
progress-ring — determinate, pair with a number
step-loader — the honest one: it names the stage
- ✓Read 1,204 rows
- Validating columns
- Import
- !Notify owners
page-loader — first paint only
optimistic-row — pending, and failed
button loading — reserves its own width, so nothing jumps
States
All of them follow one rule: say what happened, then offer the way out. Note that “nothing here yet” and “your filter matched nothing” are different screens — the first is an invitation and carries the primary action, the second is a correction and carries a way back.
first-run — an invitation
No processes yet
When you map your first process, it will show up here.
no-results — a correction, echoing the query
Nothing matches “invocing”
Try a shorter search, or remove a filter.
error-state — inline failure
That didn't work
Something went wrong on our side. Trying again usually fixes it.
success-state
Your process map is ready
We found three bottlenecks worth costing.
offline-banner — watches the connection
error-boundary — never a blank white screen
This part of the page stopped working
The rest of the app is still fine.
Full-page 404 / 500 / 403 / maintenance live on the screens page.
Every indicator above degrades to a still, legible form under
prefers-reduced-motion — the spinner keeps its accent arc, skeletons hold at 75% opacity,
the bar fills. Someone who cannot tolerate motion must still be able to tell that something is loading.