<!-- Protocore Design System — Spinner -->
# Spinner

- **Category:** Feedback (`feedback`)
- **Slug:** `feedback/spinner`
- **Status:** stable
- **Platforms:** web, mobile
- **Import:** `import { Spinner } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/feedback/spinner

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

## 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](/feedback/progress-bar) instead — a spinner that never ends reads as a hang.
- Replacing a specific block of content that is about to appear? Use [LoadingSkeleton](/feedback/loading-skeleton) — it holds the layout and reduces the shift when data lands.
- Inside a submitting button? [Button](/inputs/button) has a built-in `loading` state; don't nest a bare Spinner in it.

## 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:

```tsx
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.

```tsx
<Spinner size="md" label="Loading deployments" />
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `label` | `string` | no | `Loading` | Accessible status label announced to assistive tech. |
| `size` | `enum` | no | `md` | Diameter preset: `sm` 14px · `md` 18px · `lg` 24px. |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

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

```tsx
import { Spinner } from "@protocore/pds";

export default function Basics() {
  return <Spinner label="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.

```tsx
import { HStack, Spinner } from "@protocore/pds";

export default function Sizes() {
  return (
    <HStack gap={6} align="center">
      <Spinner size="sm" label="Loading" />
      <Spinner size="md" label="Loading" />
      <Spinner size="lg" label="Loading" />
    </HStack>
  );
}
```

## Do & don't

**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

**Notes**

- 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.

## Related

`progress-bar`, `loading-skeleton`, `button`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
