Navigation

v0.0.4
Feedback

Spinner

A fine ring for work of unknown duration.

On this page

A spinner is a fine ring for work of unknown duration. Its arc fades along its length, so it has a leading end and no hard tail. It takes the current text color, so it reads as ink on moonstone and pearl on obsidian.

Spinner

<span class="spinner" role="status" aria-label="Loading"></span>

#Sizes

Sizes

<span class="spinner spinner-sm" role="status" aria-label="Loading"></span><span class="spinner" role="status" aria-label="Loading"></span><span class="spinner spinner-lg" role="status" aria-label="Loading"></span><span class="spinner spinner-xl" role="status" aria-label="Loading"></span>

#With text

When a visible label describes the work, hide the spinner from assistive technology and announce the label instead.

With text

<p role="status" class="flex items-center gap-xs m(0) text-sm fg-text-muted"><span class="spinner" aria-hidden="true"></span>Uploading 3 files…</p>

#Accessibility

  • Give a standalone spinner role="status" and an aria-label.
  • Use aria-hidden="true" when nearby text already describes the progress, as in a busy button.
  • With reduced motion preferred, the ring turns more slowly.

#Classes

ClassDescription
spinnerThe ring, 16px, in the current text color
spinner-sm12px; the default is 16px. With spinner
spinner-lg24px; the default is 16px. With spinner
spinner-xl36px; the default is 16px. With spinner
spinner-inlineAs large as the icons of the control around it, or 1em where no control sets an icon size. With spinner

#Custom properties

PropertyDefaultDescription
--spinner-size1remDiameter of the ring; a control may set it for the spinner it holds
--icon-sizeRead from the control around the spinner; the stylesheet falls back to 1em. On spinner-inline

#Source

@master/ui/components/spinner.css
/* * Spinner: a fine ring for work of unknown duration. It takes the current text color. * The arc fades along its length, so it has a leading end and no hard tail. */@layer components {  .spinner {    --spinner-size: 1rem;    --spinner-width: max(1.5px, calc(var(--spinner-size) / 9));    display: inline-block;    flex: none;    width: var(--spinner-size);    height: var(--spinner-size);    border-radius: 999px;    background: conic-gradient(from 90deg, color-mix(in oklab, currentColor 12%, transparent) 0 18%, currentColor);    /* Cut the disc down to a ring; the half-pixel ramps keep both edges of the ring smooth. */    mask: radial-gradient(farthest-side, transparent calc(100% - var(--spinner-width) - .5px), black calc(100% - var(--spinner-width)), black calc(100% - .5px), transparent);    vertical-align: -.125em;    animation: spinner-rotate .72s linear infinite;    /* Inside a control, the spinner follows the control's icon size. */    &.spinner-sm { --spinner-size: .75rem; }    &.spinner-lg { --spinner-size: 1.5rem; }    &.spinner-xl { --spinner-size: 2.25rem; }    &.spinner-inline { --spinner-size: var(--icon-size, 1em); }    @media (prefers-reduced-motion: reduce) {      animation-duration: 1.8s;    }    @media (forced-colors: active) {      background: conic-gradient(from 90deg, GrayText 0 18%, CanvasText);      forced-color-adjust: none;    }  }}@keyframes spinner-rotate {  to {    rotate: 360deg;  }}