For the complete documentation index, see 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.
dithercn

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.

Controls
Animation
Fill
Colour
Cell2px
Duration1.8s

Installation

pnpm dlx shadcn@latest add https://dithercn.dev/r/skeleton.json

Usage

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

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.

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.

Props

PropTypeDefault
shape

A rectangle, lines of text, or an avatar.

Type: "block" | "text" | "circle"Default "block"
lines

Text only. How many lines; the last one is shorter.

Type: numberDefault 3
size

Circle only. Diameter in pixels.

Type: numberDefault 40
color

A palette name such as "ink" or "blue", a CSS variable name, or any CSS colour.

Type: DitherColorDefault "grey"
fill

Texture of the resting layer: a Bayer ramp from the left, a dot lattice, diagonals, or a plain tint.

Type: "gradient" | "dotted" | "hatched" | "solid"Default "gradient"
animation

A dense band sweeping across, the dither stepping up and down through its levels, or nothing.

Type: "sweep" | "pulse" | "none"Default "sweep"
cell

Size of one dither cell in CSS pixels.

Type: numberDefault 2
duration

Seconds for one sweep or pulse, including the pause after it.

Type: numberDefault 1.8
delay

Seconds to wait before the first sweep. Offset it per row for a wave.

Type: numberDefault 0

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.