Spinners are what an interface shows while it waits: a shape moving at a constant rate to say work is in flight without pretending to know when it ends. This family carries both halves of waiting. Three indeterminate sweeps, built from eight arms, eight dots, and a pair of opposed arcs over a soft ring, sit beside a progress ring small enough to live in a list row, a queue of bars with a total and an estimate, a rolling percentage for a blocking wait, and a panel that names each step as it runs.
Choosing between them is a question about duration, not style. A wait too short to notice is better served by nothing at all, since a spinner that appears and vanishes reads as a glitch. A wait long enough for someone to wonder whether it has hung needs more than a sweep, because a spin that never changes looks identical to a freeze. That is where elapsed time, a percentage, or a named step list earns its place.
Best for
Work with no measurable end: a request in flight, a permission check
Anatomy
Eight arms · eight dots · a soft ring under two opposed arcs · all three sweeping at a constant rate · a hint message under each
Preparing your workspace
Syncing 12 projects
Checking permissions
Best for
Limits and quotas: a ring small enough to sit in a row and still carry its own number
Anatomy
Plan card with its badge · a row per limit: ring, name, used of total · 6.72px track on a 20.64px radius, round-capped from twelve o'clock · the percentage inside, or not
Resets on 1 October
Best for
A long job with a real end: an import, a migration, a download
Anatomy
Queue card with its status badge · a row per item: name, size, and queued / percent / done · full-round 6px track with the brand fill · a footer carrying the total and the estimate
Started a few seconds ago
4,812 rows · about 2 minutes left
Best for
The one number a full-screen or blocking wait is about
Anatomy
A 0-9 column per place, rolling only when its own digit turns, faded at the slot edges · the heading and what to expect under it · the one way out
Keep this tab open: the file downloads on its own when it’s ready.
Best for
A job with named steps, where the wait needs to say what it is doing, not just that it is
Anatomy
Title and elapsed time across the full header · a step list marking done, running and pending · one segmented bar and its readout under it, all off one value
Started a few seconds ago
A form button that has been pressed and is waiting on the server. The spinner replaces the label or sits beside it, the button goes disabled, and a second press cannot create a second record.
A billing page listing each limit with a small ring beside it. The rings are scanned at a glance for the ones near the top, and the used-of-total figure next to each one carries the detail.
Files processed one after another, each with its own bar and status and a footer totalling the work left. The queue answers which item is slow, which a single aggregate bar cannot.
A blocking wait during an account or data move, where the percentage is the whole message. The heading says what is running, the rolling number carries the progress, and one control offers the way out.
An indeterminate spinner moves at a constant rate and carries no value; it says only that work is happening. A determinate one, a ring or a bar that fills, reports a fraction of a known total. The rule of thumb is that if the fraction cannot be computed honestly, do not draw one, because a determinate ring that stalls is read as a broken product.
Yes. A continuous rotation is exactly the sustained motion that the reduced-motion preference exists to suppress. Offer a version that fades or pulses rather than sweeping, or drop the animation and lean on the text beside it. A spinner should not be the only signal that a wait is under way in any case: a line of text naming what is being waited on serves everyone.
Join the Orvain Dispatch and get production-ready components, free to keep: React and Vue code plus the Figma frame, yours to ship.
Spinners are what an interface shows while it waits: a shape moving at a constant rate to say work is in flight without pretending to know when it ends. This family carries both halves of waiting. Three indeterminate sweeps, built from eight arms, eight dots, and a pair of opposed arcs over a soft ring, sit beside a progress ring small enough to live in a list row, a queue of bars with a total and an estimate, a rolling percentage for a blocking wait, and a panel that names each step as it runs.
Choosing between them is a question about duration, not style. A wait too short to notice is better served by nothing at all, since a spinner that appears and vanishes reads as a glitch. A wait long enough for someone to wonder whether it has hung needs more than a sweep, because a spin that never changes looks identical to a freeze. That is where elapsed time, a percentage, or a named step list earns its place.
Best for
Work with no measurable end: a request in flight, a permission check
Anatomy
Eight arms · eight dots · a soft ring under two opposed arcs · all three sweeping at a constant rate · a hint message under each
Preparing your workspace
Syncing 12 projects
Checking permissions
Best for
Limits and quotas: a ring small enough to sit in a row and still carry its own number
Anatomy
Plan card with its badge · a row per limit: ring, name, used of total · 6.72px track on a 20.64px radius, round-capped from twelve o'clock · the percentage inside, or not
Resets on 1 October
Best for
A long job with a real end: an import, a migration, a download
Anatomy
Queue card with its status badge · a row per item: name, size, and queued / percent / done · full-round 6px track with the brand fill · a footer carrying the total and the estimate
Started a few seconds ago
4,812 rows · about 2 minutes left
Best for
The one number a full-screen or blocking wait is about
Anatomy
A 0-9 column per place, rolling only when its own digit turns, faded at the slot edges · the heading and what to expect under it · the one way out
Keep this tab open: the file downloads on its own when it’s ready.
Best for
A job with named steps, where the wait needs to say what it is doing, not just that it is
Anatomy
Title and elapsed time across the full header · a step list marking done, running and pending · one segmented bar and its readout under it, all off one value
Started a few seconds ago
A form button that has been pressed and is waiting on the server. The spinner replaces the label or sits beside it, the button goes disabled, and a second press cannot create a second record.
A billing page listing each limit with a small ring beside it. The rings are scanned at a glance for the ones near the top, and the used-of-total figure next to each one carries the detail.
Files processed one after another, each with its own bar and status and a footer totalling the work left. The queue answers which item is slow, which a single aggregate bar cannot.
A blocking wait during an account or data move, where the percentage is the whole message. The heading says what is running, the rolling number carries the progress, and one control offers the way out.
An indeterminate spinner moves at a constant rate and carries no value; it says only that work is happening. A determinate one, a ring or a bar that fills, reports a fraction of a known total. The rule of thumb is that if the fraction cannot be computed honestly, do not draw one, because a determinate ring that stalls is read as a broken product.
Yes. A continuous rotation is exactly the sustained motion that the reduced-motion preference exists to suppress. Offer a version that fades or pulses rather than sweeping, or drop the animation and lean on the text beside it. A spinner should not be the only signal that a wait is under way in any case: a line of text naming what is being waited on serves everyone.
Join the Orvain Dispatch and get production-ready components, free to keep: React and Vue code plus the Figma frame, yours to ship.