# EmptyState

> Placeholder block for empty lists, zero search results, and first-run screens.

- Status: stable
- Group: display
- Page: https://www.digitaltableteur.com/design-system/components/empty-state
- Contract: https://www.digitaltableteur.com/contracts/v1/EmptyState.contract.json

## When to use

EmptyState fills the space where content should be with an explanation and a way forward: a decorative icon, a real heading, up to two sentences of description, and an action slot for Buttons. Use sm inside panels and cards, md in content areas, lg on full pages. For failures use AlertBanner; EmptyState is for legitimate absence.

## When not to use

- Do not use EmptyState for errors or loading; AlertBanner owns failures and Skeleton owns loading.

## Look-alikes

- AlertBanner: Do not use EmptyState for errors or loading; AlertBanner owns failures and Skeleton owns loading.
- Center: Use it for single focal content: Spinner, EmptyState, a logo lockup.
- Skeleton: Do not use EmptyState for errors or loading; AlertBanner owns failures and Skeleton owns loading.

## Props

| Prop | Type | Default | Description | Required |
| --- | --- | --- | --- | --- |
| `icon` | `string` | none | Phosphor icon name rendered decoratively above the title. | No |
| `title` | `string` | none | Short headline stating what is empty. | Yes |
| `description` | `string` | none | One or two sentences explaining why, or what to do next. | No |
| `headingLevel` | `"h2" \| "h3" \| "h4"` | `h2` | Heading tag for the title. Defaults to `h2`; set it to fit the page outline. | No |
| `size` | `"sm" \| "md" \| "lg"` | `md` | Size token. Defaults to `md`. | No |
| `children` | `React.ReactNode` | none | Action slot; typically one primary Button, optionally a secondary. | No |

## Variants

- Available sizes: `sm`, `md`, `lg` (default `md`).
- Allowed `headingLevel` values: `h2`, `h3`, `h4` (default `h2`).

## Invalid combinations

The contract records no invalid combinations.

## Keyboard

| Key | Behaviour |
| --- | --- |
| Tab | Reaches actions in the slot. |

## Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

| Level | Rule | How it is checked |
| --- | --- | --- |
| MUST | ARIA: Title is a real heading; headingLevel must fit the page outline. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: Icon is decorative (aria-hidden) | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by `npm run test:stories:matrix:ci`. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by `npm run test:stories:matrix:ci`. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by `npm run test:stories:hc`. |
| MUST | Every documented keyboard interaction works as specified. | Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-11-04. |
| SHOULD | Name what is empty in the title ("No projects yet"), not a generic "Nothing here". | Not stated in the contract. |
| SHOULD | Give a way forward: an action button, or a description telling the user what will populate this space. | Not stated in the contract. |
| SHOULD | Set headingLevel so the title fits the page outline (h3 inside a card that sits under an h2). | Not stated in the contract. |
| SHOULD | Keep zero-search-results copy actionable: suggest clearing filters or broadening the term. | Not stated in the contract. |
| SHOULD NOT | Do not stack more than two actions; one primary path plus one escape hatch. | Not stated in the contract. |

## Install and import

- Install: `npm install @digitaltableteur/react` (version 0.9.1, restricted access on npm).
- Import: `import { EmptyState } from "@digitaltableteur/react";`
- Entry point: `@digitaltableteur/react/feedback` exports it too.
- Styles: import `@digitaltableteur/react/style.css` once at the app boundary.
- Also available as the custom element `<dt-empty-state>`; see the custom elements manifest.

## Links

- Machine-readable contract (Design System Contract 1.0): https://www.digitaltableteur.com/contracts/v1/EmptyState.contract.json
- Markdown version of this page: https://www.digitaltableteur.com/design-system/components/empty-state.md
- Component index as JSON: https://www.digitaltableteur.com/design-system/components.json
- All components: https://www.digitaltableteur.com/design-system/components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.
