Skip to content

HySkeleton ​

Usage examples on this page are written for Lit / plain HTML (<hy-skeleton>). The same component ships as HySkeleton in @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 loading attribute (hy-button).
  • A short operation or an inline loading indicator, with no content to preview: hy-spinner.
  • A very long operation: hy-progress with a percentage.

Examples ​

Basic rectangle skeleton ​

html
<hy-skeleton></hy-skeleton>

Circle skeleton (avatar placeholder) ​

html
<hy-skeleton shape="circle" width="48px" height="48px"></hy-skeleton>

Text skeleton with multiple lines ​

html
<hy-skeleton shape="text" lines="3"></hy-skeleton>

Different animation effects ​

html
<hy-skeleton effect="sheen"></hy-skeleton>
<hy-skeleton effect="pulse"></hy-skeleton>
<hy-skeleton effect="none"></hy-skeleton>

Loading toggle with content ​

html
<hy-skeleton loading>
  <p>This content shows when loading is false</p>
</hy-skeleton>

Card skeleton with aria-busy on container ​

html
<!-- 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 ​

html
<!-- 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 ​

PropertyAttributeTypeDefaultDescription
shapeshape'rectangle' | 'circle' | 'text''rectangle'The shape of the skeleton placeholder.
effecteffect'sheen' | 'pulse' | 'none''sheen'The animation effect for the skeleton.
widthwidthstring | undefined'100%' (rectangle/text) or the medium avatar size, --hy-icon-size-md (circle)Width of the skeleton. Accepts any valid CSS value.
heightheightstring | undefined'1rem' (rectangle) or same as width (circle)Height of the skeleton. Accepts any valid CSS value.
radiusradiusstring | undefined—Border radius for rectangle shape. Accepts any valid CSS value.
loadingloadingbooleantrueWhether the skeleton is in loading state.
lineslinesnumber1Number of text lines to render.

Slots ​

SlotDescription
defaultContent to display when loading is false

CSS Parts ​

PartDescription
baseThe skeleton element container
skeletonThe skeleton placeholder element
lineIndividual 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.

PropertyDefaultDescription
--hy-skeleton-height—Height of the rectangle skeleton
--hy-skeleton-icon-size-mdvar(--hy-icon-size-md)Size of the circle skeleton
--hy-skeleton-text-line-height—Height of each text line
--hy-skeleton-gap-layout-xsvar(--hy-gap-layout-xs)Gap between text lines
--hy-skeleton-motion-loop-pulse-durationvar(--hy-motion-loop-pulse-duration)Duration of skeleton animation
--hy-skeleton-motion-loop-pulse-easingvar(--hy-motion-loop-pulse-easing)Easing of the skeleton loading animation

Built with Lit. Documented with VitePress.