What a screen shows when its data is missing, slow, failing, too long or not yours to change.
A screen is designed with its data in place, and then spends much of its life without it: nothing added yet, a search that matches nothing, a request on its way or one that failed, a name longer than its column. Each of these has a shape in the kit. Decide every one before a screen ships, even when the decision is that it can't happen.
Every screen that shows data owes these states. Write down how each one looks, or why it never occurs.
Zero rows can mean three things. On first use nothing has been added yet, so say what the place is for. With no results the data is there and a search hides it, so name the search. After someone removes everything, keep it calm: a line and a quiet way to add one again, without the welcome.
A first-use empty state names the place, says in one line why it is worth filling, and offers one action.
When a search or filter hides everything, the data still exists. Name the query and offer to clear it, never to create.
Show nothing for the first 250ms, since most requests finish by then. After that, draw skeletons the size of what is coming and hold them for at least 400ms, so a quick answer doesn't flash. A spinner is for an action in progress, and it sits in the button that started it.
Rows load as rows of the same height, so nothing moves when the data lands.
An action in progress shows a spinner in the button that started it, with a label that says what is happening. The rest of the screen stays readable.
An error says what failed and what to do next, in plain words, at the place it happened. What the person typed stays where it was. Never "Oops" or "Something went wrong": neither tells anyone what to do. When a whole list fails to load, Empty variant="error" takes its place with the same two things.
Name what didn't load or save, keep the input, and make retrying a button rather than an instruction to reload.
The error sits under the field it belongs to and says what a valid value looks like.
Real names are longer than the ones in the design. Try every row at 320px wide with the longest value you can find. Text gives way, figures don't.
A long name ends in an ellipsis on one line, and the whole name shows on hover and focus. The figure beside it stays whole.
Experiment, 3 variants
Experiment, 3 variants
Someone who can't take an action still sees it, turned off, with the reason and who can do it. A control that quietly disappears gives nobody a reason to ask. When a whole page is off limits, Empty variant="no-permission" says so and names who can grant access.
Put the reason next to the control in text. A disabled button can't show a tooltip, so a reason on hover never appears.
Only workspace admins can delete experiments.
A figure is a claim. When a value is missing, show a dash, never 0: zero says it was measured and came out empty. When the sample is small, say how small, so nobody ships a winner on eight visits.
Not measured yet and measured at zero are different facts. Show a dash, and say when the value will exist.
| Paywall shown | 3,120 |
| Trial started | 214 |
| PaidKnown when trials end on day 7 | Not known yet |
| Paywall shown | 3,120 |
| Trial started | 214 |
| Paid | 0 |
A rate from eight people looks as solid as one from eight thousand. Show the count beside the rate, and mark a result that is too early to call.