# Loader

A small dithered loading indicator: a ring with a comet tail, three dots that dissolve in turn, a pulse, or a sweeping bar.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The dithercn agent skill, for building any dither on the engine, is at [/.well-known/agent-skills/dithercn/SKILL.md](/.well-known/agent-skills/dithercn/SKILL.md).

<ComponentPreview src="examples/loader-demo.tsx" size="sm" controls="loaderControls">
  <LoaderDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="loader" />

## Usage [#usage]

```tsx
import { Loader } from "@/components/dithercn/loader";

export function Saving() {
  return <Loader size="sm" color="blue" label="Saving changes" />;
}
```

Each variant is a tone field on a small grid of cells. Every frame, the loader thresholds the field with the Bayer matrix and paints the inked cells into a canvas at one pixel per cell. Shape edges are hard, so the ring and the dots keep clean outlines; only the tone inside them changes, so the motion happens in the dither. Cells are 2 pixels by default, big enough that you see the Bayer steps.

* `ring`: a thick ring with a comet on a faint track. The head is solid and the tail steps down through the dither levels behind it. The tail stretches and shrinks as it goes round, so the ring breathes instead of spinning at one pace.
* `pulse`: a dot that beats while three rings ripple out from it and thin out as they grow. Good for "waiting for something to arrive".
* `dots`: three discs that build up from sparse dither to solid and dissolve again, one after another. Twice as wide as it is tall.
* `bar`: a dense head easing across a faint track, its tail thinning behind it. Three times as wide as it is tall.

## Examples [#examples]

### Variants and sizes [#variants-and-sizes]

`sm`, `md` and `lg` are 24, 40 and 64 pixels tall. Cells default to 1 pixel at `sm` and 2 pixels above it, so larger loaders show more cells and smoother falloff.

<ComponentPreview src="examples/loader-variants.tsx" size="md">
  <LoaderVariants />
</ComponentPreview>

### In context [#in-context]

A ring next to a deploy, a bar under an upload, and a pulse in an empty event feed that is waiting for its first event. Give each one a `label` that says what is loading.

<ComponentPreview src="examples/loader-in-context.tsx" size="lg">
  <LoaderInContext />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"ring" \| "dots" \| "pulse" \| "bar"` | `"ring"` | Which animation to draw. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Height of 24, 40 or 64 pixels. |
| `label` | `string` | `"Loading"` | What screen readers announce. |
| `color` | `DitherColor` | `"ink"` | A palette name such as "green", a CSS variable name, or any CSS colour. |
| `bloom` | `"off" \| "low" \| "high"` | `"off"` | A soft glow in the loader's colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. |
| `cell` | `number` | `2 (1 at sm)` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | Speed multiplier. |

## Notes [#notes]

* The root has `role="status"` and a visually hidden `label`, so screen readers announce it politely when it appears. The canvas is hidden from them.
* With reduced motion turned on, each variant draws one still frame that still reads as a loader.
* It pauses when scrolled out of view or when the tab is hidden. A loader is at most about a thousand cells, so a frame is cheap, and it uses a 2D canvas rather than WebGL, so loaders never count against the page's WebGL budget.
* Cells snap to whole device pixels, so a loader stays crisp at any zoom level.