Skip to content
EspañolEnglish
All components (204)

Skeleton

Surfaces · Skeleton · .amset-skeleton

What it is and when to use it

A gray placeholder, with a shine that passes over it, while the content arrives. It holds the place with the shape of what is going to arrive (lines of text, a title, a block, a circle), so that the page does not jump on load.

When to use it

  • Loads of more than an instant where the shape of what is coming is known: a list, a detail page, a card.
  • shape="text" (default) draws lines lines, with the last one shorter; title, block and circle are a single piece.
  • size (from xs to xl) gives it the thickness of a text of that size and, in circle, the side of the Avatar of the same size: the slot does not jump when the photo arrives.
  • width, height and radius to fit it to what it replaces (an avatar, a photo, a card). In text, the width is that of the paragraph and the height and the radius are those of each line.

When not to

  • A task that advances and ends (uploading a file, importing): Progress.
  • Waiting for a button to respond: Button with loading.
  • A dashboard figure: Stat with loading, which already reserves the slot of the number.

Keyboard: it is not focusable.

Accessibility: it is decorative on purpose (aria-hidden): hearing "image, image, image" helps nobody. What says that something is loading is the container, with aria-busy (Card sets it with loading), or a status text. With reduced motion it stays still and without shine; withoutAnimation keeps it still always, in all shapes (in text, all the lines).

Fine-tuning: CSS variables --amset-skeleton-*: -duration (one pass of the shine, two turns of --amset-duration-spin), -color (10% of the ink over whatever is behind: it looks the same over the background, over a card or over an alternate row), -shine, -radius, -line, -title and -circle. In md: line of 0.75rem, title of 1.25rem at 40% of the width, block of 8rem and circle of 2.25rem, that of the avatar. The shine moves with transform, without repainting.

What it looks like

<Skeleton shape="title" width="12ch" />
<Skeleton shape="text" lines={3} />

Props

Import it with import { Skeleton } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
classNamestringMerged with the library classes; it does not replace them.
heightstringHeight of the gap; in the `text` shape, that of each line.
linesnumberHow many lines, for the `text` shape. The last one comes out shorter.
radiusstringOwn radius: to match the card or avatar it replaces. In the `text` shape, that of each line.
shapeSkeletonShape—
size"sm" | "md" | "lg" | "xs" | "xl"What it stands in for, at the same size: the thickness of the lines of `text` and `title`, and the diameter of the `circle`, which is that of the `Avatar` of the same size (so the space does not jump when the photo arrives).
widthstringWidth of the gap; in the `text` shape, that of the whole paragraph.
withoutAnimationbooleanStill, with no shimmer; in the `text` shape, all the lines. With reduced motion it already stays still on its own.

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

CSS variables

These CSS variables are how you adapt it. Declare them on the component's selector, not on :root: the stylesheet redeclares them in the rule itself, and a declaration on the same element always wins.

VariableDefault
--amset-skeleton-durationcalc(var(--amset-duration-spin) * 2)
--amset-skeleton-colorcolor-mix(in oklab, var(--amset-ink) 10%, transparent)
--amset-skeleton-shinecolor-mix(in oklab, var(--amset-surface) 55%, transparent)
--amset-skeleton-radiusvar(--amset-radius-sm)
--amset-skeleton-line0.75rem
--amset-skeleton-title1.25rem
--amset-skeleton-circle2.25rem

How to change it

.amset-skeleton {
  --amset-skeleton-duration: ...;
  --amset-skeleton-color: ...;
}

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: