HySkeleton
Usage examples on this page are written for Lit / plain HTML (
<hy-skeleton>). The same component ships asHySkeletonin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A skeleton loading placeholder that indicates content is being loaded.
Skeleton components display a placeholder preview of content before the data loads, reducing perceived load time and preventing layout shift. They're ideal for content-heavy pages where loading spinners would be too intrusive.
Key Features:
- Three shape variants (rectangle, circle, text)
- Three animation effects (sheen, pulse, none)
- Customizable dimensions via props or CSS custom properties
- Loading toggle to show/hide skeleton vs content
- Multi-line text skeleton support
- SSR compatible
Accessibility: built in
- Uses
aria-hidden="true"on skeleton visuals (decorative) - Respects
prefers-reduced-motion(disables animations) - High contrast and forced colors mode support
- Not focusable (purely visual indicator)
Accessibility: your part
- Set
aria-busy="true"on the region the skeletons fill, and remove it when the content arrives. - Keep focusable elements out of a region that shows only skeletons.
Usage with aria-busy: Set aria-busy="true" on a parent container (not on the skeleton itself) to indicate loading state to assistive technologies. Remove or set to "false" when loading completes. Note: Screen reader support for aria-busy is limited (primarily JAWS 2020+).
When to Use:
- Page/section loading (< 10 seconds expected)
- Content placeholders for cards, lists, text
- Initial data fetch on page load
- Lazy-loaded content areas
When to use
- A region whose loaded layout is known: a list, a card grid, a profile.
Do
- Match the shape and the number of lines of the content that will replace it.
When not to use
- A load that failed or stalled: replace the skeletons with
hy-empty-state state="error". - A button's loading state or a form submission: the button's own
loadingattribute (hy-button). - A short operation or an inline loading indicator, with no content to preview:
hy-spinner. - A very long operation:
hy-progresswith a percentage.
Examples
Basic rectangle skeleton
<hy-skeleton></hy-skeleton>Circle skeleton (avatar placeholder)
<hy-skeleton shape="circle" width="48px" height="48px"></hy-skeleton>Text skeleton with multiple lines
<hy-skeleton shape="text" lines="3"></hy-skeleton>Different animation effects
<hy-skeleton effect="sheen"></hy-skeleton>
<hy-skeleton effect="pulse"></hy-skeleton>
<hy-skeleton effect="none"></hy-skeleton>Loading toggle with content
<hy-skeleton loading>
<p>This content shows when loading is false</p>
</hy-skeleton>Card skeleton with aria-busy on container
<!-- Set aria-busy on the container, not the skeleton -->
<div class="card" aria-busy="true" aria-label="Loading card content">
<hy-skeleton shape="rectangle" height="200px"></hy-skeleton>
<hy-skeleton shape="circle" width="40px" height="40px"></hy-skeleton>
<hy-skeleton shape="text" lines="2"></hy-skeleton>
</div>Fill a sized container
<!-- The crop box has a definite height (aspect-ratio); height="100%"
stretches the skeleton to fill it with zero layout shift. -->
<style>
.crop {
aspect-ratio: 1 / 1;
}
</style>
<div class="crop">
<hy-skeleton shape="rectangle" height="100%"></hy-skeleton>
</div>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
shape | shape | 'rectangle' | 'circle' | 'text' | 'rectangle' | The shape of the skeleton placeholder. |
effect | effect | 'sheen' | 'pulse' | 'none' | 'sheen' | The animation effect for the skeleton. |
width | width | string | undefined | '100%' (rectangle/text) or the medium avatar size, --hy-icon-size-md (circle) | Width of the skeleton. Accepts any valid CSS value. |
height | height | string | undefined | '1rem' (rectangle) or same as width (circle) | Height of the skeleton. Accepts any valid CSS value. |
radius | radius | string | undefined | — | Border radius for rectangle shape. Accepts any valid CSS value. |
loading | loading | boolean | true | Whether the skeleton is in loading state. |
lines | lines | number | 1 | Number of text lines to render. |
Slots
| Slot | Description |
|---|---|
default | Content to display when loading is false |
CSS Parts
| Part | Description |
|---|---|
base | The skeleton element container |
skeleton | The skeleton placeholder element |
line | Individual text line element (for shape="text") |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-skeleton-height | — | Height of the rectangle skeleton |
--hy-skeleton-icon-size-md | var(--hy-icon-size-md) | Size of the circle skeleton |
--hy-skeleton-text-line-height | — | Height of each text line |
--hy-skeleton-gap-layout-xs | var(--hy-gap-layout-xs) | Gap between text lines |
--hy-skeleton-motion-loop-pulse-duration | var(--hy-motion-loop-pulse-duration) | Duration of skeleton animation |
--hy-skeleton-motion-loop-pulse-easing | var(--hy-motion-loop-pulse-easing) | Easing of the skeleton loading animation |