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

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

> A scrim + centered spinner that covers its positioned parent and blocks interaction during loading.

## When to use it

**LoadingOverlay** is for a *bounded* region that is busy — a card refreshing its data, a form submitting, a panel re-fetching. It sits above that region's own content so the user keeps their spatial context, unlike a full-page loader.

Use it when the wait is short and you have nothing partial to show. If you can show the *shape* of the incoming content, a [LoadingSkeleton](/feedback/loading-skeleton) is less jarring. If the work is long and measurable, prefer a [ProgressBar](/feedback/progress-bar). Always give the overlay a positioned parent — it is `position: absolute` and fills its nearest positioned ancestor.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `blur` | `boolean` | no | `false` | Blur the content behind the scrim (backdrop-filter). |
| `className` | `string` | no | — | — |
| `label` | `string` | no | `Loading` | Status text announced to assistive tech and shown under the spinner if `showLabel`. |
| `showLabel` | `boolean` | no | `false` | Render the label visibly beneath the spinner (otherwise it is screen-reader-only). |
| `size` | `enum` | no | `md` | Spinner diameter preset. |
| `spinner` | `ReactNode` | no | — | Replace the default spinner with your own indicator (e.g. a ProgressBar). |
| `style` | `CSSProperties` | no | — | — |
| `visible` | `boolean` | no | `true` | Whether the overlay is shown. When `false`, nothing renders. |

## Examples

### Basics

Mount it inside a `position: relative` container and toggle `visible`. It covers the parent with a canvas scrim and a centered spinner, blocking clicks on the content beneath.

```tsx
import { useState } from "react";
import { LoadingOverlay, Button, Card, Stack, Text, Heading } from "@protocore/pds";

export default function Basics() {
  const [visible, setVisible] = useState(false);
  const run = () => {
    setVisible(true);
    setTimeout(() => setVisible(false), 1600);
  };
  return (
    <Stack gap={4} align="start">
      <Button variant="secondary" size="sm" onClick={run}>
        Refresh ledger
      </Button>
      <Card style={{ position: "relative", minWidth: 280 }}>
        <LoadingOverlay visible={visible} />
        <Heading as="h4">Treasury</Heading>
        <Text>Balance 1,204,882 PROTO across 3 accounts.</Text>
      </Card>
    </Stack>
  );
}
```

### Blur and a visible label

Set `blur` to frost the content behind the scrim, and `showLabel` to render the status text beneath the spinner. Otherwise the label is announced to screen readers only.

```tsx
import { LoadingOverlay, Card, Text, Heading } from "@protocore/pds";

export default function Blur() {
  return (
    <Card style={{ position: "relative", minWidth: 280 }}>
      <LoadingOverlay visible blur showLabel label="Syncing" />
      <Heading as="h4">Epoch 812</Heading>
      <Text>Aggregating validator attestations for the current epoch.</Text>
    </Card>
  );
}
```

## Do & don't

**Do**

- Give the parent `position: relative` so the overlay fills that region.
- Use it for short, indeterminate waits on a bounded surface.
- Set `blur` when the content behind would distract.
- Swap in a determinate indicator via `spinner` when progress is known.

**Don't**

- Mount it on a positioned parent, or it covers the wrong box.
- For long, measurable work, show progress instead.
- Where content could show as a skeleton, use that instead.
- Keep the announced label; the scrim alone doesn't convey "loading".

## Accessibility

**Notes**

- The scrim is `role="presentation"` with `aria-busy="true"`, marking the region as busy without adding a spurious landmark.
- It contains a polite `role="status"` live region carrying the `label` (default "Loading"), so assistive tech announces the busy state.
- By default the label is visually hidden; set `showLabel` to render it beneath the spinner as mono UPPERCASE text.
- The overlay covers the region visually but does not trap focus — disable or `inert` the underlying controls if they must not be reachable while busy.
- The spinning ring is `aria-hidden`; the announcement comes from the status label, not the animation.

## Related

`spinner`, `loading-skeleton`, `progress-bar`, `transition`

---

© 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/
