Menu


Spinner


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.

Customize the preview


Choices
Flags
View
Loading results

Options


Spinner options
ChoiceValuesDefaultWhat it does
sizesmbk-spinner--size-<value>smmdlgsm
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>defaultsubtledefault
Default draws the ring in the main icon color; subtle in the quieter one, for a wait beside secondary text.
labelsmbk-spinner--labelOn or offOff
Shows the words beside the ring. Without it they are still read aloud, and they show anyway when the reader asks for reduced motion.

Partssmbk-spinner__ringsmbk-spinner__label

Examples


Usage

The ring with its words beside it, at the small size, the default.

Loading projects

Sizes

Three sizes, the icon sizes, so a spinner can stand where an icon of the same step would.

LoadingLoadingLoading

Tones

The default ring in the main icon color, and a subtle one for a wait beside quieter text.

Checking the addressUploading

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.

Loading the activity feed

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.
Loading projects
Do

Say what is being worked on, so the reader knows what they are waiting for.

Please wait
Don't

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
spinner.css
/* 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
Spinner tokens
Token and CSSValuePreviewFoundation
space.inline.xs--smbk-space-inline-xs4pxSpacing
color.icon.default--smbk-color-icon-defaultlight: #232527dark: #e7ebf0changes with themeColor
color.icon.subtle--smbk-color-icon-subtlelight: #4e555edark: #c4cdd8changes with themeColor
size.icon.md--smbk-size-icon-md20pxSize
border-width.emphasis--smbk-border-width-emphasis2pxBorder width
radius.full--smbk-radius-full9999pxRadius
motion.spin.duration--smbk-motion-spin-duration1000msMotion
size.icon.sm--smbk-size-icon-sm16pxSize
size.icon.lg--smbk-size-icon-lg24pxSize
color.text.subtle--smbk-color-text-subtlelight: #4e555edark: #9ba5b1changes with themeColor
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-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px

The quick brown fox

Typography
Manifest
spinner.manifest.json
{
  "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"
}