Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/skeleton.jsonUsage
import { Skeleton } from "@/components/asciicn/skeleton";
export function PostLoading() {
return (
<div aria-busy="true" className="flex flex-col gap-3">
<span className="sr-only">Loading post</span>
<Skeleton rows={6} />
<Skeleton shape="text" lines={3} />
</div>
);
}A skeleton is rows of ░ the width of its container. Set the width with a class such as w-32; the height is rows cells, so it lines up with the glyph grid. shape="text" draws lines rows with a gap between them and a shorter last line, and shape="circle" draws a disc size rows across for avatars.
While it waits, a band of ▒▓▒ sweeps from left to right. The band's position comes from the viewport and a shared clock, so the sweep passes over every skeleton on the page as one light rather than each one starting on its own. motion="pulse" steps every glyph between ░ and ▒ instead. Swap the glyphs with chars, from resting to peak: "·•●" makes a dot skeleton, "⠂⠕⠿" a Braille one.
Examples
Cards and a panel
Each card keeps its real label while its value loads. The wide panel below uses chars="·•●", so it sweeps in dots instead of shades.
Table
Rows of a member table: a two-row disc for the avatar and single-row lines of different widths. One band crosses every cell of the table in step.
Props
| Prop | Type | Default |
|---|---|---|
| shape A filled rectangle, lines of text, or a disc. | Type: "block" | "text" | "circle" | Default "block" |
| rows block: height in rows of cells. | Type: number | Default 3 |
| lines text: number of lines. The last one is shorter. | Type: number | Default 3 |
| size circle: diameter in rows of cells. | Type: number | Default 3 |
| motion A dense band sweeping across, every glyph pulsing, or still. | Type: "sweep" | "pulse" | "none" | Default "sweep" |
| chars Glyphs from resting to the peak of the sweep. | Type: string | Default "░▒▓" |
| color Glyph colour, a palette name or any CSS colour. | Type: AsciiColor | Default "grey" |
| cell Glyph cell height in CSS pixels. | Type: number | Default --ascii-cell |
Notes
- Skeletons are
aria-hidden. Mark the region that is loading witharia-busy="true"and give it a short status in text, such as a visually hidden "Loading post". - The first render, on the server too, is plain rows of the resting glyph, so there is no flash and no layout shift when the sweep starts.
- The sweep redraws at 30fps, writing text straight into each line, and pauses offscreen and in hidden tabs. With reduced motion it holds the resting glyph.