Components / feedback

Skeleton

It belongs to the loading pattern more than to itself, which is why the guidance that matters is on the Components page under Loading rather than here.

Rendered from the package · open on its own

Summary

The shape of content that has not arrived.

APG patternnone
Built onnative elements
Statusstable
Since0.2.0
<Skeleton style={{ inlineSize: '60%' }} />
<Skeleton style={{ blockSize: '8rem' }} />

Anatomy

A <span data-se="skeleton" aria-hidden="true">. It has no parts — it is one block that takes its size from where you put it.

_No named parts. The frame is the whole anatomy._

Properties

_None. Everything it renders comes from its children._

States

_None. Rest is the only condition it has._

Behavior

It pulses until it is replaced.

Accessibility

Always aria-hidden, and it is not optional. A skeleton is a picture of a paragraph. Announcing it would read a layout to someone who asked for content. The announcement belongs to whatever owns the load: a Spinner beside it, or aria-busy on the region being filled.

Keyboard

None of its own. It is not focusable and takes no keys.

Roles, states and properties

AttributeSet by
aria-hiddenalways true

Focus

Never takes focus, and is out of the accessibility tree entirely.

Screen reader

Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.

Contrast and target size

npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.

It sets no target size of its own; anything interactive inside it brings its own.

Tested

  • test/browser/feedback.spec.mjs

Known gaps

_No upstream requirement is recorded as not applicable._

Appearance

TokenUsed for
--se-duration-slowanimation
--se-ease-standardanimation
--se-radius-smborder-radius
--se-surface-sunkbackground

Motion

Opacity pulses to 45% and back over four times --se-duration-slow, forever. The reduced-motion reset stops it after one pass, leaving a flat block — which is still a legible placeholder.

No shimmer sweep. A gradient travelling across a page of skeletons is a lot of movement to say "not yet".

Content

None. If a placeholder needs words, the honest words are an EmptyState.

Usage

It takes its size from where you put it. The only useful skeleton is one shaped like the thing it stands in for — a skeleton the wrong shape moves the page when the content lands, which is the reflow it existed to prevent.

Match the count, not just the shape. Three lines of skeleton for one line of text is a worse guess than no skeleton. If the number of rows is unknown, one block is honest and five are a lie.

When not to use it. For a load under a second, nothing is better than either a skeleton or a spinner. For anything the reader triggered and is waiting on, use the control's own busy state.

Notes

It belongs to the loading pattern more than to itself, which is why the guidance that matters is on the Components page under Loading rather than here.