Empty state
What a listing, a panel or a tab says when it has nothing to show — and, in the error tone, when loading it failed. React packages/ui/src/components/EmptyState.tsx, CSS .acrm-empty* in apps/web/src/ds.css.
Until 12 September 2026 these were two components with two geometries. They are now one (AD, ER-2): tone="empty" (default) and tone="error". ErrorState, ds/error-state.css and every .est* class are gone; 92 call sites moved across in one chain of merge requests.
The most-used state primitive in the system, so the wording carries most of the weight: say what is missing, and where it makes sense offer the one action that fills it.
Tones
- Empty — the slot loaded correctly and holds nothing. No role, no announcement; it is a normal part of the page.
- Error — the load failed. Same block, same geometry, plus: the icon turns
--critical-ink, the wrapper carriesrole="alert", and the component supplies its own wording (Načtení selhalo/Data se nepodařilo načíst…) and its own action label (Zkusit znovuwith therefreshicon), so a screen writes onlyaction={{ onClick }}.
Loading is not a tone of this block — that is skeleton-line / skeleton-list, which keeps the layout instead of replacing it.
When to use
- A listing, a tab, a rail section or a panel that loaded correctly and has no items — empty tone.
- A filter or a search that matched nothing — with an action that clears the filter, never one that creates a record.
- A state that is good news (
Vše přečteno) — then with no action at all. - A fetch for a listing, a panel, a tab or a preview failed — error tone with a retry.
- A save failed and the change is still recoverable — the retry repeats the save.
- A record cannot be shown for a reason the user can act on (no permission) — error tone without an action, because repeating will not help.
When not to use
- The data is on its way — that is
skeleton-listorskeleton-line. - A whole record detail that does not exist — that is
detail-state-screen. - A field-level validation message — that belongs at the field.
- A confirmation of something that worked — that is
toast.
Anatomy
div.acrm-empty(add.smfor the dense variant,.errorfor the error tone) centred inside its host horizontally and vertically (RULES §27, owner 21 Oct 2026): the host fills the rest of the page, list or panel below its top bar, tabs and filter bar, and the state sits in the middle of it..smchanges padding only, never the type sizes..acrm-empty-ic— the icon, 24px (--ico-xl, a token added for this on 12 September 2026, ACRM-228). The rule is written assvg.acrm-empty-icon purpose: the global.tirule inapp-shell.cssloads afterds.cssand would otherwise win on equal specificity — that is how the icon rendered 16px for weeks while the code declared 20..acrm-empty-t— the title: what is missing, or what failed, in one short line..acrm-empty-s— optional second line: what will make it appear.actionrenders a real<Button>— outlined by default,ghostwhere the action is secondary to the message. Withoutactionno button is drawn (five screens used to draw a dead „Zkusit znovu").action.menu(FA204, 26 September 2026) renders a canonical Menu button instead — the same label and glyph with a trailing chevron, opening a menu of sources („Nahrát audio“ → Nahrát soubor / Z Plaudu). One item draws a plain Button that runs it. Never compose a menu under the empty state by hand.secondaryrenders a second, quieter way out next to it; the two sit in.acrm-empty-act. The action row stands 16px (--sp-5) below the text (ACRM-229).
Measured on main after V2 (AD, 12 September 2026, ACRM-228/229/230): padding 40/16 (sm 16/12), gap 6 between icon, title and sub-line, icon 24 × 24 above the title, title var(--h2) (500 15/18.75) in --fg-secondary, sub-line var(--subtitle) (400 13/19.5) in --fg-muted, 16 above the action, button 30, max width 340. Block height 204 (was 187). The same values apply to both tones and to sm, which changes padding only.
Best practices
Do
- Put the state inside the frame it belongs to — the listing keeps its top bar, the tab keeps its tabs.
- Centre it vertically in the area it replaces (RULES §27): the host takes the remaining height (
flex: 1; min-height: 0in a column flex) and the state sits in the middle; taller content starts at the top and scrolls. Only a section that is sized by its content (rail section, card) stays top-aligned. - Offer at most one action, and only when it genuinely fills the emptiness or repeats what failed.
- For "nothing found", make the action clear the filter — creating a record is the wrong answer to a filter that hid one.
- Say what failed when the screen knows (
Výpis obchodů se nenačetl), not just "data". - Drop the action when retrying cannot help — a button that reliably fails again is worse than no button.
- Use
smin panels, rail sections and cards; the words stay the same, only the air goes. - Hide a header's own "create" button while its list is empty and let the empty state's
actioncarry it instead (owner 25 Sep 2026,NotesTabBody— shared by the notes tab on person, company, deal and meeting): an empty surface with the same action offered twice reads as two different things to try.
Don't
- Leave a full-page or full-list state hanging under the top bar with empty space below it.
- Explain the feature. An empty state is not onboarding, and long text goes unread (AD 9 Aug 2026).
- Draw an illustration.
- Blur the two tones. "Nothing here" and "we could not load it" are different facts and the user acts differently on each — the tone is the difference, the wording must carry it too.
- Show a stack trace, an error code or a request id in the sub-line.
- Replace the whole page when only one panel failed — the state belongs in the slot that failed.
- Render several error tones into one region at once;
role="alert"is a live region and they queue up. - Show either tone while the request is still running.
Content
- Title: the missing thing or the failure, named as the user names it (
Žádné úkoly, notSeznam je prázdný). - Sub-line: what makes it appear, or why it failed and what to do — one sentence.
- Action label: the verb of the action being repeated (
Zkusit znovu,Uložit znovu); the error tone fills it in for you. - Sentence case, no exclamation marks, no full stop in the title.
Related
skeleton-line,skeleton-list— the same slot while loading.detail-state-screen— a whole record detail with nothing to show.toast— a failure the user should notice without losing the screen.button— the action inside.menu-button— the action inside when it opens a menu (action.menu).- Stories:
components--empty-state--default,--variants(both tones),--sizes,--states,--composition.
Source: docs/empty-state/empty-state.usage.md · GitLab