Skip to content
Glint UI

Skeleton

A pulsing placeholder for content that has not arrived, sized where you use it.

Usage

import { Skeleton } from "@glint/components/skeleton.slint";
import { Tokens } from "@glint/theme/tokens.slint";
export component AppWindow inherits Window {
width: 560px;
height: 200px;
background: Tokens.color-background;
VerticalLayout {
alignment: center;
padding: 24px;
Skeleton {
width: 100%;
height: 20px;
}
}
}

Skeleton renders an animated pulsing rectangle used as a placeholder during asynchronous data loading.

Examples

Complex loading card

Compose skeletons of various dimensions to simulate card content, media, and buttons before data resolves.

API Reference

Properties

PropertyTypeDefaultDescription
radiusin lengthTokens.radius-mdCorner radius of the placeholder.
pulseout floatno defaultThe phase the pulse is in — 1.0 at full, 0.45 at its dim end. Published because the pulse is this component's whole behaviour and the element drawing it sits below the root, out of reach.

Accessibility

  • A skeleton is silent, and that is the problem it hands you. It declares no accessible-role and takes no focus: a screen reader finds a rectangle with nothing in it, so a reader who cannot see the pulse is told nothing at all — not that the content is coming, and not that it has arrived.
  • Announce the wait somewhere that can. The usual answer is a Spinner, which ships accessible-role: progress-indicator, the label "Loading" and a polite live region, placed where the skeleton is or beside it. Where the skeleton is the whole surface, name the region it stands in and put the state in its label, both properties together at the call site:
import { Skeleton } from "@glint/components/skeleton.slint";
import { Tokens } from "@glint/theme/tokens.slint";
export component AppWindow inherits Window {
width: 320px;
background: Tokens.color-background;
in property <bool> loading: true;
Rectangle {
accessible-role: region;
accessible-label: root.loading ? @tr("Loading messages") : @tr("Messages");
accessible-live-region: AccessibleLiveness.polite;
VerticalLayout {
padding: 16px;
spacing: 8px;
Skeleton { height: 12px; }
Skeleton { height: 12px; }
}
}
}
  • Swap it for the content, not on top of it. A skeleton left in the tree behind the loaded content is a second copy of the region for anyone reading it linearly.