Skip to content
Protocore Design Systemv1.6.9

/// Feedback

Spinner

A minimal 1em border spinner that announces an indeterminate loading status inline.

import { Spinner } from "@protocore/pds";
View as Markdown

Basics

A single spinner. It carries role="status" and a visually-hidden label so assistive tech announces the wait.

Loading ledger

Sizes

Three diameters — sm (14px), md (18px), lg (24px). The spinner scales to 1em, so it also inherits the font size of its context.

LoadingLoadingLoading

Inline with a label

Pair the spinner with visible text for anything longer than a beat, so sighted users get the same context the label gives screen readers.

ProvisioningProvisioning node eu-central-1a…

When to use it

Reach for a Spinner when the wait is short and its length is unknown — a button submitting, a panel fetching, a row saving. It says *something is happening* without implying a measurable amount.

  • Known, measurable progress (upload, migration, sync)? Use ProgressBar instead — a spinner that never ends reads as a hang.
  • Replacing a specific block of content that is about to appear? Use LoadingSkeleton — it holds the layout and reduces the shift when data lands.
  • Inside a submitting button? Button has a built-in loading state; don't nest a bare Spinner in it.

Usage

Do

  • Give every Spinner a meaningful `label` ("Loading ledger", not the default).
  • Use it for short, indeterminate waits where progress can't be measured.
  • Match the size to the surrounding text or control.
  • Show visible text alongside it once a wait exceeds a second or two.

Don't

  • Use a Spinner for long or measurable work — that's a ProgressBar.
  • Cover a whole page in one when a skeleton would preserve the layout.
  • Render more than one Spinner for a single logical wait.
  • Nest a Spinner inside a Button that already has a `loading` prop.

Accessibility

  • The root is `role="status"` (an `aria-live="polite"` region), so the `label` is announced when the spinner mounts.
  • The spinning ring is `aria-hidden`; only the visually-hidden label text is exposed.
  • The animation respects `prefers-reduced-motion` via the shared PDS keyframes.
  • A Spinner alone is not focusable and takes no keyboard interaction — it is a status indicator, not a control.

Mobile (React Native)

Preview. @protocore/pds-mobile ships the React Native sibling of Spinner. It mirrors the web API where React Native allows; the package is a preview with no device-level QA yet, so pin it and expect small changes.

Import it from the mobile package (not @protocore/pds), inside a <PdsProvider> — there is no stylesheet, so style (a ViewStyle) replaces className and every value comes from the theme:

import { Spinner } from "@protocore/pds-mobile";

Parity with web. Wraps the native ActivityIndicator (web is a CSS border-spinner).

  • size maps to a dp diameter (sm 14 · md 18 · lg 24) and is tinted with ink.
  • label is announced to assistive tech.
<Spinner size="md" label="Loading deployments" />

Spinner props

PropTypeDefaultDescription
classNamestring
labelstringLoadingAccessible status label announced to assistive tech.
sizeenummdDiameter preset: `sm` 14px · `md` 18px · `lg` 24px.
styleCSSProperties

Related

  • ProgressBarA thin track that fills to value/max, or sweeps indeterminately when value is omitted.
  • LoadingSkeletonShimmering placeholder blocks that hold a layout while its real content loads.
  • ButtonThe system action control — mono uppercase label, sharp corners, and a primary fill that inverts on hover.