Design system/Components
Empty and loading states
A page that has nothing yet shows an empty box and one sentence: what's missing and how to create the first one. A page that's loading shows the shape of what's coming.
In codecomponents/empty-box.tsxcomponents/knowledge/ui.tsx (SkeletonRows)components/load-more.tsx
Empty state
EmptyState draws an open, empty box in outline, in the tertiary text color, so it's correct in both themes. Below it, one sentence and, if needed, an action. Every list used to write its own centered paragraph, and no two empty pages matched.
art="deal" is for places where the first thing is revenue-related: the first company, the first project, the first account. That illustration is shown exactly as drawn.
No projects yet. Create your first so your actions have somewhere to go.
Nothing found for this phrase.
Loading
For lists, skeleton rows shaped like the real rows; the page doesn't jump when data arrives. For a button that performs an action, a small spinner in place of the label. For long lists, "More" at the bottom instead of loading everything up front.
Rules
Do
"No files yet. Drag your first one and drop it here."
Don't
"No data found."
Do
A filter that found nothing: "Nothing matches that search."
Don't
The same "You haven't created anything yet" box when the user has only filtered.
Do
A skeleton shaped like the real row.
Don't
A big spinner in the middle of the page for every load.
<EmptyState className="card" action={
<button className="btn btn-dark" onClick={create}>{tr("ساخت پروژه")}</button>
}>
{tr("هنوز پروژهای نداری.")}
</EmptyState>
<SkeletonRows count={4} />Sizes
112pxsize prop; EmptyBox itself 132--text-tertiary · stroke 312px · --text-secondary · leading 24px · max-width 320pxpadding 32px 16px · text-center52px · padding-inline 26px · 15.5px / 600margin-top 16px; phone full-widthheight round(size × 1.3)28 × 28px r 9 · 11px and 9pxgap 10px between rows--muted → --border · 1.3s ease-in-out15px · border 2px · 0.6s linear.spinner-dark 20px.pill · arrow 11px“* left” 11pxAccessibility
- The box illustration is aria-hidden and focusable="false"; the sentence is what gets read.
- SkeletonRows has aria-busy="true" and the aria-label “Loading...”.
- The empty state sentence is --text-secondary at 12px: 5.14 to 1 on a light card and 5.8 in dark, AA in both. The sentence is short, and its button says what to do.
- If the deal image doesn't load, the box takes its place; a broken image icon on an empty page looks like a bug.
- With reduced motion, the skeleton shimmer and spinner rotation drop to almost zero.
Do
Pass the empty state button only through action; CSS sets its size.
Don't
btn-sm in an empty state; the page's only action looks small.
Related pages