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: SkeletonList sets aria-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-busy flips, 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