USER INTERFACE
Spinners
For short waits of unknown length — and the rules for when not to show one at all.THEME
DENSITY
01
Playground
Kind, size, speed, colour, label and show-after delay0delay 300 ms1.6 s
Spinner shows from 300 ms until the data arrives at 1 sREQUEST
<Spinner
kind="ring" size={32} thickness="reg" speed="normal"
tone="brand" aria-label="Loading invoices"
delay={300} minDuration={400}
/>
Settings
KIND
SIZE
THICKNESS · SPEED
COLOUR
LABEL
SHOW AFTER
02
Gallery
Seven kinds, three sizes, three backgroundsKINDON CARDON LIGHTON PHOTO
Ringdefault · anywhere
Arclarger areas
Dotstyping · inline
Pulselive · listening
Barsaudio · syncing
Orbitbackground jobs
Logofirst app load
A A A
A A A
A A A
03
In context
Buttons, fields, rows, cards, first load, chat, pull to refreshIn a buttonwidth locked · double-click safe
Width stays 160 px — nothing jumps
In a fielddebounced search · right side
Type to search — the spinner waits 350 ms for you to stop typing
In a table rowone row syncs, others stay usable
NTSpring 27 campaignSynced 2 h ago
KCWebsite v2Synced yesterday
FOParka lookbookSynced 3 d ago
LCOctober retainerSynced just now
Card overlaykeep old data, dim, spin
REVENUE · 30 DAYS
€72,900▲ 12% vs last 30 days
Updated 2 min ago
Full page · first loadbrand mark · then content
AVAVA
Good morning, OliviaReload to see the brand mark
Typing indicatorthree dots in a bubble
Could we get the Spring 27 selects by Friday?
Pull to refreshdrag the list down
Lena commented on Hero 0420 mAna uploaded 12 files1 hInvoice AV-041 viewed2 hJon booked Studio A3 hKaya signed the proposalMon
04
Behaviour
Timing, failure and successDelay & minimum300 ms delay · 400 ms minimum · hint at 3 s
Fast · 150 msnothing shown
Medium · 600 msspinner 400 ms min
Slow · 5 sspinner + hint
0 s1 s2 s3 s4 s5 s
■ request■ spinner▨ “taking longer”
Error → Retryspinner becomes a warning
⇣
InvoicesFirst try fails on purpose.Success morphring closes into a check
Send AV-2026-042
05
Rules & a11y
Markup, checklist and what to use insteadMarkup
<section aria-busy="true" aria-describedby="inv-loading"> …previous rows stay visible… </section> <div id="inv-loading" role="status" aria-live="polite"> <span class="spinner" aria-hidden="true"></span> <span class="sr-only">Loading invoices</span> </div> <button aria-busy="true" disabled> <span class="spinner" aria-hidden="true"></span> Saving… </button>
Checklist7 of 8 checked
NOTHINGUnder ~300 ms. Any indicator would only flash.Filter changes, tab switches
SPINNER0.3–3 s, unknown length, small area.Save, search, sync a row
SKELETONYou know the layout of what’s coming.Lists, cards, first page load
PROGRESSOver ~3 s or measurable work.Uploads, exports, reports
DO✓Delay 300 ms, then keep it 400 ms✓Put the spinner where the result will appear✓Keep old content visible and dimmed while refreshing✓Say what is loading — “Loading invoices”✓Turn it into ✓ or ⚠ when it ends
DON’T✕Block the whole page for a small update✕Show two spinners for the same request✕Spin forever — add a timeout and Retry✕Change button width when it starts spinning✕Use a spinner for work that takes minutes