Skip to main content

Skeleton

Info:Feedback html svelte astro Success: coverage 16/16

A shimmering loading placeholder in four shapes: text, line, block, and circle.

Live demo

live · @xoji/astro

Skeleton

Four shapes

text
line
block
circle

Text in three sizes

sm
md
lg

A loading card

Skeleton stands in for content that has not loaded yet, smoothing the perceived wait with a gentle shimmer that sweeps between two surface tints. Pick a shape to match the content it foreshadows (text for a run of copy, line for a thin rule or divider, block for a card or media region, circle for an avatar) and a size to scale it.

The element is purely decorative and marks itself aria-hidden; the surrounding container owns the busy state via aria-busy, so assistive tech announces "loading" once rather than narrating every placeholder. Width is left to the caller (set it inline or via a class) so a skeleton can mirror the real content's measure.

When to use

How this component composes with the rest of the set.

Compose several skeletons inside one container that carries aria-busy="true"; swap the whole container for real content when it loads.
Set width inline (style="width: 60%") or via a layout class to mirror the measure of the content being awaited.
Mirror a Card's layout with a circle avatar plus stacked text lines for a faithful loading silhouette.

Props

2 props, straight from the manifest.

PropTypeDefaultBindingsDescription
shape SkeletonShape
text line block circle
text
html svelte astro
The placeholder geometry: what kind of content it stands in for.
size Size
sm md lg
md
html svelte astro
Scales the placeholder's height (and diameter, for circle).

Appearance

Variants

text

.xoji-skeleton--text

A single line of body-height copy with a soft corner radius.

line

.xoji-skeleton--line

A thin pill standing in for a rule, divider, or caption underline.

block

.xoji-skeleton--block

A tall rectangle for a card, image, or media region.

circle

.xoji-skeleton--circle

A perfect circle for an avatar or icon placeholder.

Sizes

sm

.xoji-skeleton--sm

Compact.

md

default
.xoji-skeleton

Default.

lg

.xoji-skeleton--lg

Large.

States

shimmer

.xoji-skeleton

The always-on loading animation sweeping a highlight across the surface; neutralized under reduced-motion by the base layer.

Anatomy

The named parts that make up the component, with their selectors.

skeleton

.xoji-skeleton

The placeholder block carrying the shape and size classes; paints and animates the shimmer.

--bg-2 --bg-3 --radius-md --duration-slow --ease-standard

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 16/16 consumed tokens produced default register: 276 tokens

Live coverage check against the xoji-default register (derive(xojiDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--bg-2 --bg-3 --border-normal --border-thick --duration-slow --ease-standard --radius-full --radius-md --radius-sm --space-1 --space-6 --space-7 --space-8 --text-body --text-lg --text-sm

Accessibility

The placeholder is decorative; it sets aria-hidden="true" so screen readers skip the individual shapes.
The loading state belongs on the surrounding container via aria-busy="true" (optionally with aria-live="polite"), not on each skeleton.
The shimmer routes through --duration-slow/--ease-standard, so the global reduced-motion base rule stills it without extra component CSS.
Carries no focusable or interactive content, so it never traps keyboard focus while content loads.

Code

A loading card

Four shapes stacked inside an aria-busy container. The canonical loading silhouette.

<div aria-busy="true" aria-live="polite">
	<xoji-skeleton shape="circle"></xoji-skeleton>
	<xoji-skeleton shape="text" style="width: 60%"></xoji-skeleton>
	<xoji-skeleton shape="text"></xoji-skeleton>
	<xoji-skeleton shape="block"></xoji-skeleton>
	<xoji-skeleton shape="line"></xoji-skeleton>
</div>
<script lang="ts">
	import { Skeleton } from "@xoji/svelte";
</script>

<div aria-busy="true" aria-live="polite">
	<Skeleton shape="circle" />
	<Skeleton shape="text" style="width: 60%" />
	<Skeleton shape="text" />
	<Skeleton shape="block" />
	<Skeleton shape="line" />
</div>
---
import { Skeleton } from "@xoji/astro";
---

<div aria-busy="true" aria-live="polite">
	<Skeleton shape="circle" />
	<Skeleton shape="text" style="width: 60%" />
	<Skeleton shape="text" />
	<Skeleton shape="block" />
	<Skeleton shape="line" />
</div>