Spinner
Spinner — the wait when the shape of what is coming is unknown.
Beta · Feedback and status · packages/css/src/components/spinner.css · React on Base UI: @pepperui/spinner
.pui-spinner · size --24 --32 · tone --default --brand --on-accent. Inside a Button: Spinner + disabled.
Sizes across, states down.
What it is for
Section titled “What it is for”- Size: 16, 24, 32 — the icon scale. 16 inside a Button (with data-icon; Button has no isLoading — compose Spinner + disabled), 24 in a card, 32 on a page
- Tone: muted (text/muted, the default), default (text/default), brand (text/link), on-accent (text/on-accent — inside a primary button; the dark backdrop on this page is specimen only)
- Motion: one rotation per second, linear, until the wait ends; with prefers-reduced-motion it slows, it does not stop
- No states, no text: the label belongs to the button or the region
Use when a wait under a few seconds with no known layout — a button submitting, a panel refreshing.
Do not use when the layout is known (Skeleton); the wait is long or measurable (Progress); there is nothing to load (Empty).
Relates to Skeleton, Progress, Button (its loading composition), Empty.
Anti-patterns a spinner larger than 32 · two spinners on one screen · a spinner with no accessible label · a spinner on a table that looks the same every time — that is a Skeleton.
Keywords: spinner, loading, loader, wait, busy
Figma — page Spinner, Skeleton & Progress: Spinner (12 variants)
shadcn: spinner
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens spinner.css.
<span class="pui-spinner pui-spinner--24" role="status" aria-label="Loading">…loader-circle svg…</span>The modifiers
Section titled “The modifiers”| What it is | Note | |
|---|---|---|
| Size | (none) 16 · --24 · --32 | — |
| Tone | (none) muted · --default · --brand · --on-accent (inside a primary button) | — |
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”No story of this component renders a text pair axe can measure — a component made of surfaces and borders rather than words. The token pairs it uses are in artifacts/contrast-matrix.md.
The focus ring
Section titled “The focus ring”Nothing in this component takes focus.
Keyboard
Section titled “Keyboard”Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.