Skeleton line — accessibility
Keyboard
- Not focusable and not interactive: it is a decorative
<span>. - Focus must not be parked on a skeleton. A host that replaces focused content should keep focus on a stable ancestor and restore it when the data lands.
ARIA
- The line itself carries no role and no label — announcing "loading" once per bar would be noise.
- The wrapper is where the state belongs:
SkeletonListsetsaria-busy="true", and any hand-built skeleton block should do the same on the container. - Do not give a skeleton
role="img"or alt text; it stands for content that does not exist yet.
Focus
- Nothing focusable, so no focus ring.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- The bar and its shimmer are drawn from the surface scale and are deliberately low-contrast — they represent absent content, not information, so the AA text thresholds do not apply.
- The bars must not be mistaken for real content: keep them out of a layout where a grey bar could read as a filled field.
Known limits
- The shimmer is a continuous animation and does not honour
prefers-reduced-motion; a user who has asked for less motion still gets the sweep. - Nothing announces the transition from loading to loaded — the wrapper's
aria-busyflips, but no live region says so. - A skeleton whose metrics do not match the real content reintroduces exactly the layout shift the primitive exists to prevent, and nothing checks that.
Source: docs/loading/skeleton-line.a11y.md · GitLab