A small turning ring that says something is being worked on, for a wait with no known end: alone, beside its words, or inside a busy button.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
sizesmbk-spinner--size-<value> | smmdlg | sm | The ring is the icon size of its step: sm sits beside small text and inside small controls, md beside body text, lg alone in a region. |
tonesmbk-spinner--tone-<value> | defaultsubtle | default | Default draws the ring in the main icon color; subtle in the quieter one, for a wait beside secondary text. |
labelsmbk-spinner--label | On or off | Off | Shows the words beside the ring. Without it they are still read aloud, and they show anyway when the reader asks for reduced motion. |
Examples
Usage
The ring with its words beside it, at the small size, the default.
Sizes
Three sizes, the icon sizes, so a spinner can stand where an icon of the same step would.
Tones
The default ring in the main icon color, and a subtle one for a wait beside quieter text.
Ring alone
Without the label flag the words are not drawn but are still read aloud. Under reduced motion the ring stops and they show.
Guidelines
Purpose
A small turning ring that says something is being worked on, for a wait whose end is not known: a search running, a list loading, a file being sent. It differs from the progress bar, which shows how much of a known amount is done, and from the skeleton, which stands in for content that is about to arrive.
When to use
- A wait of a second or more with no known end, in a region the reader is looking at: a list that is loading, a panel being refreshed.
- Beside its words, in a row that reports a state: "Uploading", "Checking the address".
- Inside a control that is waiting for its results, such as a search box while it searches.
When not to use
- Inside a button whose action is running. Use the button's loading flag, which keeps the button's width and name and says it is busy.
- When the amount of work is known. Use the progress bar, so the reader can see how far along it is.
- When the content's shape is known before it arrives. Use the skeleton, so the page does not jump.
- For a wait shorter than about a second. Show nothing; a ring that flashes is noise.
Usage rules
Do
- Write the words as what is happening: "Saving changes", "Loading projects".
- Replace the spinner with the result as soon as it is ready.
- Offer a way out when the wait runs long.
Don't
- Put a spinner inside a button by hand. Use the button's loading flag.
- Show several spinners for one wait.
- Leave a spinner turning with no end and no way to recover.
Say what is being worked on, so the reader knows what they are waiting for.
A bare “Please wait” says something is happening but not what.
Anatomy
- The block, an inline row that sits on the line of the text around it.
- The ring part, two arcs facing each other on a circle, turning together. It is decorative: a screen reader never reads it.
- The label part, the words that say what is happening. Always present; drawn beside the ring when the label flag is on, and read aloud either way.
Behavior
The ring turns at an even speed for as long as the spinner is shown. When the reader has asked their system for reduced motion, the ring stands still and the words show beside it, so the words say what is happening when nothing moves.
A spinner has no end of its own. If the wait passes a few seconds, the page shows what is happening and offers a way out, such as a message with a Try again button. A spinner that never stops, with no way to recover, is not supported.
Two tones: the default ring is the main icon color; the subtle ring is quieter, for a wait beside secondary text. Three sizes match the icon sizes, so a spinner can take the place of an icon of the same step.
Accessibility
What it does for you
- The spinner is a status region: a screen reader reads its words when it appears, without moving focus. The ring is hidden from assistive technology.
- Without words, the wrapper says "Loading" and warns in development.
- Under reduced motion the ring stops and the words show.
What you must do
- Give it its words, even when they are not drawn. Say what is happening, such as "Loading results", not just "Please wait".
- Announce completion or failure once, from the page: replace the spinner with the result, or with a message that says what went wrong.
How to test
- With a screen reader, the words are read when the spinner appears, and focus does not move.
- Turn on reduced motion in the system: the ring stands still and the words show beside it.
- When the wait ends, the page says how it went, once.
Source
The files the spinner is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-spinner — Spinner. Figma "Spinner" 7110:461: Size × Tone × Reduced motion, 12 variants; the words are content.
* A ring of two arcs that turns for a wait with no known end, alone, beside words, or inside a button while its
* action runs.
* Reduced motion is not an axis: under the reader's setting the ring stands still and the words show, so they
* carry the meaning when nothing moves. */
.smbk-spinner {
display: inline-flex;
align-items: center;
gap: var(--smbk-space-inline-xs);
vertical-align: middle;
color: var(--smbk-color-icon-default);
}
/* The block frames the words it hides for screen readers, so they stay inside it wherever it is placed: without a
frame they hang from the page itself and stretch it, past the end of an area that scrolls. No weight, so a parent
that places the block keeps its say. */
:where(.smbk-spinner) {
position: relative;
}
.smbk-spinner--tone-subtle {
color: var(--smbk-color-icon-subtle);
}
/* The ring: two arcs facing each other, the top and bottom quarters of a circle drawn as the box's top and bottom
edges in the current color, the side edges clear, turning once per spin duration at an even speed (an eased
turn would speed up and slow down). The box is the icon size of its step, so the ring stands where an icon of
that step would and nothing around it moves while it turns. The arcs keep the emphasis border's width at every
size. A ring written as the notched-circle icon, as before, draws its arcs the same way and no glyph. */
.smbk-spinner__ring {
box-sizing: border-box;
flex: none;
width: var(--smbk-size-icon-md);
height: var(--smbk-size-icon-md);
border: var(--smbk-border-width-emphasis) solid transparent;
border-block-color: currentColor;
border-radius: var(--smbk-radius-full);
color: inherit;
font-size: 0;
animation: smbk-spinner-turn var(--smbk-motion-spin-duration) linear infinite;
}
.smbk-spinner--size-sm .smbk-spinner__ring {
width: var(--smbk-size-icon-sm);
height: var(--smbk-size-icon-sm);
}
.smbk-spinner--size-lg .smbk-spinner__ring {
width: var(--smbk-size-icon-lg);
height: var(--smbk-size-icon-lg);
}
@keyframes smbk-spinner-turn {
to {
transform: rotate(1turn);
}
}
.smbk-spinner__label {
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
white-space: nowrap;
}
/* Without the label flag the words are still in the page, for a screen reader, but not drawn. */
.smbk-spinner:not(.smbk-spinner--label) .smbk-spinner__label {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
border: 0;
}
/* Reduced motion: the ring stands still and the words show. */
@media (prefers-reduced-motion: reduce) {
.smbk-spinner__ring {
animation: none;
}
.smbk-spinner:not(.smbk-spinner--label) .smbk-spinner__label {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.inline.xs--smbk-space-inline-xs | 4px | Spacing | |
| color.icon.default--smbk-color-icon-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| color.icon.subtle--smbk-color-icon-subtle | light: #4e555edark: #c4cdd8changes with theme | Color | |
| size.icon.md--smbk-size-icon-md | 20px | Size | |
| border-width.emphasis--smbk-border-width-emphasis | 2px | Border width | |
| radius.full--smbk-radius-full | 9999px | Radius | |
| motion.spin.duration--smbk-motion-spin-duration | 1000ms | Motion | |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| size.icon.lg--smbk-size-icon-lg | 24px | Size | |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
Manifest
{
"name": "spinner",
"block": "smbk-spinner",
"summary": "A small turning ring that says something is being worked on, for a wait with no known end: alone, beside its words, or inside a busy button.",
"adapt": "css",
"axes": {
"size": {
"values": [
"sm",
"md",
"lg"
],
"default": "sm",
"description": "The ring is the icon size of its step: sm sits beside small text and inside small controls, md beside body text, lg alone in a region."
},
"tone": {
"values": [
"default",
"subtle"
],
"default": "default",
"description": "Default draws the ring in the main icon color; subtle in the quieter one, for a wait beside secondary text."
}
},
"flags": {
"label": {
"description": "Shows the words beside the ring. Without it they are still read aloud, and they show anyway when the reader asks for reduced motion."
}
},
"parts": [
"ring",
"label"
],
"figmaNodeId": "7110:461"
}