v1.0.0

<bmx-skeleton>

The grey shapes that hold a layout together while its content is on its way.

5 properties · 0 events · 0 methods · 2 parts

Example

Only the avatar carries a label, and that is the rule: one announcement per region, on the outermost or the first, and none on the rest. The whole graphic is aria-hidden, so a card with twelve of these in it is silent instead of reading out twelve grey rectangles — but something has to say the content is coming, or a page that is loading is indistinguishable from a page that is empty.
Shimmer, pulse and still. Under prefers-reduced-motion all three are the still one, and nothing is lost by that — a skeleton says what it says through its shape, and a shape survives being motionless. That is where it differs from an indeterminate <bmx-progress>, which cannot stop moving without looking like a page that has hung.
Show markup
<div class="row" style="align-items: flex-start; gap: 1rem">
  <bmx-skeleton shape="circle" label="Loading the profile"></bmx-skeleton>
  <div style="flex: 1 1 20rem">
    <bmx-skeleton lines="1" style="--bmx-skeleton-line-height: 1rem; --bmx-skeleton-gap: 0.75rem"></bmx-skeleton>
    <div style="block-size: 0.75rem"></div>
    <bmx-skeleton lines="3"></bmx-skeleton>
  </div>
</div>

<div class="row" style="margin-block-start: 1.5rem">
  <span class="note">
    Only the avatar carries a <code>label</code>, and that is the rule: one announcement per region, on the outermost or
    the first, and none on the rest. The whole graphic is <code>aria-hidden</code>, so a card with twelve of these in it
    is silent instead of reading out twelve grey rectangles &mdash; but something has to say the content is coming, or a
    page that is loading is indistinguishable from a page that is empty.
  </span>
</div>

<div class="row" style="margin-block-start: 1rem; gap: 1.5rem; align-items: flex-start">
  <div style="flex: 1 1 12rem">
    <bmx-skeleton shape="block" style="--bmx-skeleton-size: 5rem"></bmx-skeleton>
    <div style="block-size: 0.75rem"></div>
    <bmx-skeleton lines="2"></bmx-skeleton>
  </div>
  <div style="flex: 1 1 12rem">
    <bmx-skeleton shape="block" animation="pulse" style="--bmx-skeleton-size: 5rem"></bmx-skeleton>
    <div style="block-size: 0.75rem"></div>
    <bmx-skeleton lines="2" animation="pulse"></bmx-skeleton>
  </div>
  <div style="flex: 1 1 12rem">
    <bmx-skeleton shape="block" animation="none" style="--bmx-skeleton-size: 5rem"></bmx-skeleton>
    <div style="block-size: 0.75rem"></div>
    <bmx-skeleton lines="2" animation="none"></bmx-skeleton>
  </div>
</div>

<div class="row">
  <span class="note">
    Shimmer, pulse and still. Under <code>prefers-reduced-motion</code> all three are the still one, and nothing is lost
    by that &mdash; a skeleton says what it says through its <em>shape</em>, and a shape survives being motionless. That
    is where it differs from an indeterminate <code>&lt;bmx-progress&gt;</code>, which cannot stop moving without
    looking like a page that has hung.
  </span>
</div>
<bmx-skeleton shape="circle"></bmx-skeleton>
<bmx-skeleton lines="3" label="Loading your messages"></bmx-skeleton>

WHY THIS IS NOT bmx-progress

It looks like a member of the same family and it is a different thing entirely, in the one way that decides where a component boundary goes: what it is to somebody who cannot see it. A progress indicator reports a position - it has a role, a range and a value, and it is worth announcing. A skeleton reports nothing. It is a picture of a paragraph that has not arrived, and to a screen reader it should not exist at all.

That is the same judgement that made bmx-tooltip and bmx-popover two components rather than one with a flag: a shape is a look and can be a property, but a difference in what the accessibility tree is told is a difference in what the element is. Folded into bmx-progress as a third shape, this would be a progressbar with no value that must not be announced - which is a contradiction that would have to be papered over on every render.

SAY IT ONCE

The whole graphic is aria-hidden, so a card with twelve skeletons in it is silent rather than reading out twelve grey rectangles. But something should say the content is coming, or a screen-reader user meets a page that is simply empty and has no way to tell whether it is loading or finished.

So label puts one polite announcement on this skeleton, and the rule is to put it on one of them - the outermost, or the first - and leave it off the rest. Announcing per skeleton is the failure this property exists to make easy to avoid.

Properties

PropertyAttributeTypeDefaultDescription
animation animation 'shimmer' | 'pulse' | 'none' 'shimmer' The idle animation. shimmer is a highlight travelling across the shape; pulse fades it in and out; none is a still grey box. Under prefers-reduced-motion all three are the still box - unlike an indeterminate progress bar, a skeleton that stops moving still says exactly what it said before, because the information is in the shape rather than in the motion.
label label string A polite announcement that content is loading. Put it on one skeleton per region and leave it off the rest. See above.
lastLine last-line number 62 How wide the last line is, as a percentage. Shorter than the others by default, because the last line of a paragraph is - and a block of identical bars reads as a table rather than as text.
lines lines number 1 How many lines to draw, in text shape.
shape shape BmxSkeletonShape 'text' What it stands in for: lines of text, a block, or an avatar.

CSS shadow parts

PartDescription
line Each bar in a text skeleton.
shape The single box or circle, in the other two shapes.

CSS custom properties

PropertyDescription
--bmx-skeleton-color The grey the shapes are drawn in.
--bmx-skeleton-duration One cycle of the animation.
--bmx-skeleton-gap The space between lines.
--bmx-skeleton-highlight The band that travels across them in shimmer.
--bmx-skeleton-line-height How thick one line of the text shape is.
--bmx-skeleton-radius The corner radius of a line or a block.
--bmx-skeleton-size The circle's diameter, and the block's height when no height is given.