Skeleton
A pulsing placeholder for content that has not arrived, sized where you use it.
import { Skeleton } from "@glint/components/skeleton.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 200px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center; spacing: 16px;
Skeleton { width: 48px; height: 48px; radius: 24px; }
VerticalLayout { spacing: 8px; alignment: center;
Skeleton { width: 200px; height: 16px; }
Skeleton { width: 140px; height: 14px; } } } }}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.
import { Skeleton } from "@glint/components/skeleton.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 260px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
Rectangle { width: 320px; background: Tokens.color-card; border-radius: Tokens.radius-lg; border-width: 1px; border-color: Tokens.color-border-hairline; padding: 16px;
VerticalLayout { spacing: 12px;
Skeleton { width: 100%; height: 80px; radius: Tokens.radius-md; }
Skeleton { width: 160px; height: 16px; }
Skeleton { width: 100%; height: 12px; }
Skeleton { width: 80%; height: 12px; } } } } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
radius | in length | Tokens.radius-md | Corner radius of the placeholder. |
pulse | out float | no default | The 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-roleand 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.