Skip to content
RU
Scale
Density
Theme
Accent

Feedback

Skeleton

The place where content has not arrived yet. A library that is about waiting needs a primitive for waiting for content.

The component
Markup
<div class="inst-stack inst-stack--tight">
  <div class="inst-skeleton inst-skeleton--title"></div>
  <div class="inst-skeleton inst-skeleton--line"></div>
  <div class="inst-skeleton inst-skeleton--short"></div>
</div>

Contract

What Required Why
aria-busy="true" on the container yes A skeleton is styling rather than a role. Without the attribute the page is simply empty to a screen reader
inst-skeleton on every bar yes The base: the surface, the radius, the pulse, a height of 1lh
The shape repeats the content to come yes A skeleton of the wrong shape lies twice

The height 1lh is the real height of a line at the current type size. So a skeleton takes exactly as much room as the text that arrives will, and nothing jumps at the substitution.

Accessibility

The region has to declare it is busy A skeleton is styling rather than a role. A container that is loading carries aria-busy="true", otherwise for a screen reader the page is simply empty
It is not read The bars are empty and hold no text: a screen reader has nothing to read in them and no need
Reduced motion The pulse slows to 3s rather than going out: an indicator of busyness has to stay visible
Amplitude The opacity does not go below 0.45: a flicker of greater amplitude irritates peripheral vision
Print It is not printed on paper: print is static, and waiting on paper is meaningless

Variants

Example
Markup
<div class="inst-stack inst-stack--tight">
  <div class="inst-skeleton inst-skeleton--title"></div>
  <div class="inst-skeleton inst-skeleton--line"></div>
  <div class="inst-skeleton inst-skeleton--line"></div>
  <div class="inst-skeleton inst-skeleton--short"></div>
</div>
Class Width The work
inst-skeleton inherited The base: the surface, the radius, the pulse. The height is 1lh
inst-skeleton--title 40% The heading of a paragraph
inst-skeleton--line 100% A full line of text
inst-skeleton--short 62% The last line of a paragraph

--short at the end: a real paragraph rarely ends exactly at the right edge, and a bar of the same length reads as a table rather than as text.

The width can be set by a rule of your own too — the modifiers are only three frequent values.

Behavior

The pulse

A skeleton pulses by opacity: from 1 to 0.45 and back over 1.6s. The keyframes are its own, running infinitely.

A travelling sheen is a gradient, and gradients are forbidden in the library. But the ban is not the whole of it: a sheen depicts motion from left to right, that is, filling, and a skeleton fills nothing. A pulse is calmer and more honest — the block does not pretend to be filling up.

Composition

In the body of a panel

In context
Output
Markup
<div class="inst-panel">
  <div class="inst-panel-header">
    <span class="inst-panel-title">Output</span>
  </div>
  <div class="inst-panel-body">
    <div class="inst-stack inst-stack--tight" aria-busy="true">
      <div class="inst-skeleton inst-skeleton--title"></div>
      <div class="inst-skeleton inst-skeleton--line"></div>
      <div class="inst-skeleton inst-skeleton--short"></div>
    </div>
  </div>
</div>

A skeleton repeats the shape of what will come. Three bars in the place of three lines are a hint; twenty bars in the place of a card with a button are noise.

API

Classes
NameWhat it does
class
inst-skeletonThe base one. Always required
modifier
inst-skeleton--titleA width of 40%
inst-skeleton--lineA width of 100%
inst-skeleton--shortA width of 62%
Tokens
NameValueWhere from
token
--surface-sunkenlight-dark(var(--n-2), var(--n-14))base +1: theme
--radius-xs4pxbase +4: scale
--ease-in-outcubic-bezier(0.65, 0, 0.35, 1)one across every cell

Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument