Skip to content

States — empty, loading, error

State block — what a data container shows before its data, while it comes, and when it fails: Empty, Skeleton and Alert composed as one component, the three states the product kit lists for every data container.

Beta · Shells and patterns · packages/css/src/components/states.css

.pui-state-block (--card) · __skeleton-row __error — Empty, Skeleton and Alert as one block. Never a Spinner for a container.

States — empty, loading, error — every size and every stateOpen in the workshop

Sizes across, states down.

  • State: empty · loading · error; Context: table · card
  • The Empty and the Alert are exposed — their title and description say what is missing or what failed and what to do; the Retry is exposed, Has action hides it (an error the user cannot retry)
  • The block replaces the container’s body, under its own header (the example); it takes the container’s width

Use when any table, list, card or panel that loads data — every data container carries all three.

Do not use when the whole page failed (a page-level Alert or an error page); a form’s field error (Field); a control’s own busy state (Spinner in a Button).

Relates to Empty, Skeleton, Alert, Spinner, Data table, Table, Card, Button - secondary, Toast - status.

Anti-patterns a spinner where a skeleton belongs · an error with no way to retry when a retry would work · ‘Error’ as a title · a skeleton that does not match the rows it stands in for · a fill changed on the instance.

Keywords: empty state, loading state, error state, skeleton, data container

Empty + Skeleton + Alert as one trio

Figma — page States — empty, loading, error: State block (6 variants)

The class, the parts, and nothing that is not in the package. The same block opens states.css.

<div class="pui-state-block"> …a .pui-empty… </div>
<div class="pui-state-block"><div class="pui-state-block__skeleton-row"><span class="pui-skeleton pui-skeleton--circle pui-skeleton--24"></span>
<span class="pui-skeleton pui-skeleton--line" style="width: 160px"></span><span class="pui-skeleton pui-skeleton--line" style="width: 80px"></span>
<span class="pui-skeleton pui-skeleton--line"></span></div> ×5 </div>
<div class="pui-state-block"><div class="pui-state-block__error"><div class="pui-alert pui-alert--danger pui-alert--outline">…</div>
<button class="pui-button pui-button--secondary pui-button--small">Retry</button></div></div>
What it is Note
Context (none) table · --card (368, the error stacks) —

Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.

ink on ground reads floor the matrix calls it
#544d4d on #ffffff 8.24 4.5 text.muted on surface.page (promised) ok
#5b0e0d on #fce9e8 11.86 4.5 Danger badge (promised) ok
#0b0202 on #ffffff 20.51 4.5 text.default on surface.page (promised) ok
#ffffff on #0b0202 20.51 4.5 Primary button label (promised) ok

4 distinct pair(s) rendered, 0 that axe calls a violation.

4 of 4 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.

Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.

Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).

Pepper UI v1.5.0

The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.