# Skeleton

Loading placeholders drawn in dither, for blocks, text lines and avatars, with a dense band that sweeps across or a pulse that steps through the Bayer levels.

> 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/skeleton-demo.tsx" size="lg" controls="skeletonControls">
  <SkeletonDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="skeleton" />

## Usage [#usage]

```tsx
import { Skeleton } from "@/components/dithercn/skeleton";

export function ProfileLoading() {
  return (
    <div aria-busy="true" className="flex items-center gap-3">
      <span className="sr-only">Loading profile</span>
      <Skeleton shape="circle" size={40} />
      <Skeleton shape="text" lines={2} />
    </div>
  );
}
```

A skeleton is a block by default: give it a size with `className`. `shape="text"` draws a stack of lines that fills its width, the last one shorter. `shape="circle"` draws an avatar of `size` pixels.

Each skeleton is drawn in one colour. At rest it is a faint tint under a Bayer ramp that thins out from the left edge, so even a still frame reads as dither. `sweep` runs a wide band across it in four slices, each a step up the Bayer ramp, so the middle of the band is densest and its sides fall off level by level. `pulse` steps the whole block up through three nested Bayer levels and back, so the dots multiply and thin out rather than fade. The default colour is grey; use your ink or a palette colour where the real content will be coloured.

## Examples [#examples]

### A dashboard that is still loading [#a-dashboard-that-is-still-loading]

Labels you already know stay as text; only the numbers and the chart are placeholders. The figures use `ink`, so they hint at the coloured values on the way, and the chart uses a `gradient` texture that thickens towards the left.

<ComponentPreview src="examples/skeleton-dashboard.tsx" size="lg">
  <SkeletonDashboard />
</ComponentPreview>

### A table that is still loading [#a-table-that-is-still-loading]

Rows offset their `delay` so the sweep runs down the table as a wave instead of every row flashing at once.

<ComponentPreview src="examples/skeleton-table.tsx" size="lg">
  <SkeletonTable />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `shape` | `"block" \| "text" \| "circle"` | `"block"` | A rectangle, lines of text, or an avatar. |
| `lines` | `number` | `3` | Text only. How many lines; the last one is shorter. |
| `size` | `number` | `40` | Circle only. Diameter in pixels. |
| `color` | `DitherColor` | `"grey"` | A palette name such as "ink" or "blue", a CSS variable name, or any CSS colour. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | Texture of the resting layer: a Bayer ramp from the left, a dot lattice, diagonals, or a plain tint. |
| `animation` | `"sweep" \| "pulse" \| "none"` | `"sweep"` | A dense band sweeping across, the dither stepping up and down through its levels, or nothing. |
| `cell` | `number` | `2` | Size of one dither cell in CSS pixels. |
| `duration` | `number` | `1.8` | Seconds for one sweep or pulse, including the pause after it. |
| `delay` | `number` | `0` | Seconds to wait before the first sweep. Offset it per row for a wave. |

## Notes [#notes]

* Skeletons are hidden from assistive technology. Put `aria-busy="true"` on the region that is loading and a visually hidden sentence such as "Loading members", as the examples do.
* With reduced motion, the sweep is removed and the pulse holds still; the texture stays, so the placeholder still reads as one.
* The sweep animates `clip-path` on masked layers, and the pulse switches masked layers on and off in steps. There is no JavaScript and no canvas, so a page full of skeletons costs one small shared `<style>` tag.