Components · Feedback

Empty states

The deliberately calm face of a region with nothing in it — <EmptyState> centers a faint icon, a title, a muted supporting line and one clear action. It is not an error look: it says “there’s nothing here yet, and here’s how to fill it.” Two flavours cover almost everything — no-data (a region that hasn’t been populated) and no-results-for-search (a query that matched nobody). The icon is decorative; the title carries the meaning.

two flavours · no-data vs no-results-for-search
Notifications 0 unread

No notifications yet

When a team lead posts an update or invites you to a rescue event, it’ll show up here.

“husky” 0 of 214

No volunteers match “husky”

Try a broader search, or clear the filters to see everyone on the roster.

Same layout, different read. No-data offers the generative move (a solid primary CTA to go make something exist); no-results offers the corrective one (a low-key ghost “clear filters” to widen the net). The active query rides in a dismissible chip in the panel head so the empty title can name it.

minimal (title only) · and the action’s interactive states
Your queue

You’re all caught up

No pending intake reviews — nice work.

Action · states
rest
:hover
:focus
ghost :hover

Icon, description and action are all optional — drop to a bare title when a full illustration would be noise (a cleared queue). The states above are pinned with an .is-hover / .is-focus class that shares the real :hover / :focus-visible rule — hover brightens the strong accent (keeps the label AA), focus paints the 2px --accent-ring.

icon?
Optional node, centered in a 32px box at --faint. Decorative — aria-hidden; the title carries meaning. Omit for the minimal state.
title
Required. The primary line — 14px, medium weight, --text. Name the emptiness (and, for a search, echo the query).
description?
A muted supporting sentence — say what would fill the space, or the one move to get there. Kept to ~42ch for readability.
action?
Usually a <Button>, spaced 16px below the copy. primary for generative (no-data), ghost for corrective (no-results).
vs ErrorState
EmptyState = “nothing here yet, and that’s fine.” For a genuine failure use ErrorState — same centered layout, a fixed --bad glyph and a retry.

Source · EmptyState.tsx (icon · title · description · action, py-12 text-center) with Button.tsx variants primary / ghost · sibling ErrorState.tsx · design-system/feedback.