--- name: empty-state-audit description: Find every screen that looks broken, dead, or dishonest when there is no data yet — rows of zeros, "no results" with no way forward, fake placeholder content, and stats that expose emptiness. Use before a launch, when building a new product, or when the first users are not converting. --- # Empty State Audit ## Install Save this file as `~/.claude/skills/empty-state-audit/SKILL.md`, or `.claude/skills/empty-state-audit/SKILL.md` to scope it to one repo. Claude Code auto-discovers it. Invoke with `/empty-state-audit` or by asking "what does my app look like with no data?". ## Why this exists Every product is empty first, and almost none are designed for it. Interfaces get built against seed data, so the state every early visitor actually sees is the one nobody looked at. Empty is not merely unpolished. It is actively read as a signal: "0 reviews · 0 downloads · 0 sellers" tells a visitor nobody else is here, and for anything social or transactional that is the strongest possible reason to leave. The product works perfectly and still loses the user. The wrong fix is as damaging as the problem. Padding an empty screen with plausible-looking placeholder data is a lie that survives into production and gets discovered exactly when someone was deciding whether to trust you. **The goal is honest and inviting, not full.** ## Step 1 — Actually run it empty Do not reason about this from the code. Get a truly empty database and look. ``` # a scratch database, migrations only, no seed DATABASE_URL="" npm run dev ``` Walk every significant screen as a signed-out visitor, then as a brand-new signed-in user. Two different empties, and the second is usually worse because the app has promised something by then. If a genuinely empty database is impractical, read the render path for each list and find what it produces at length zero. Slower and less reliable, so prefer the real thing. ## Step 2 — Find the zeros ``` rg -n "\.length\b" --type-add 'ui:*.{tsx,jsx,vue,svelte}' -tui rg -n "(count|total|downloads|reviews|sales|members|followers|views)\b" -tui rg -n "toFixed|toLocaleString|formatCount|formatNumber" -tui ``` For each number that reaches the screen, ask what it renders at zero, then classify: - **HIDE.** A stat that is zero for everyone new should not be displayed. "0 downloads" is worse than no downloads line at all, because it converts absence into a measured failure. - **REFRAME.** "New" instead of "0 reviews". "Just listed" instead of "0 sales". True, and it turns a negative into a neutral. - **KEEP.** Zero is genuinely informative: an unread badge, a cart, an error count. The rule of thumb: **a zero that reflects YOUR product's youth should be hidden or reframed. A zero that reflects the USER's own state should usually be kept.** ## Step 3 — Judge every empty list For each list, table, feed, and grid, find the zero-length branch. A good empty state answers three things in one screen: what belongs here, why it is empty, and what to do next. Grade each one: - **MISSING** — renders nothing. The page looks broken or half-loaded. - **DEAD END** — says "No results" and stops. Technically correct, useless. - **GOOD** — explains, and offers the single most useful next action. Search results deserve their own pass, because they carry a signal the visitor cannot see: "No results for X" is ambiguous between *we have nothing like X* and *we have nothing at all*. Say which. If the catalog is nearly empty, saying so plainly is better than letting someone conclude their query was wrong. ## Step 4 — Hunt the dishonest fixes The failure mode that costs the most trust. Search for content that looks like data but is not: ``` rg -n "(placeholder|dummy|sample|example|lorem|coming soon|John Doe|Jane Smith)" -tui rg -n "(testimonial|as seen|trusted by|used by)" -tui ``` Flag anything that a visitor could reasonably read as real: - Fake avatars, names, or testimonials. - "Trusted by" logos for companies that are not customers. - Inflated or rounded-up counts, and the classic `Math.max(count, 47)`. - Ghosted skeleton rows that never resolve, implying content is loading that does not exist. Each of these has to go. Not be improved, go. A fabricated signal is discovered precisely when someone is evaluating whether to trust you, and it costs more than the empty screen ever did. ## Step 5 — Check what the empty state promises An empty state usually contains your only call to action, so it is a conversion surface, not a fallback. Check that its action is possible right now: do not offer "Browse popular items" with no items, or "Invite your team" before teams exist. Prefer the action that makes the emptiness go away, and make it the only one. Two competing buttons on an empty screen is a decision the visitor should not have to make. ## Step 6 — Report ``` Screens audited empty: DISHONEST <- remove today -> what a visitor would wrongly believe LOOKS BROKEN renders nothing at length 0 DEAD ENDS "" -> no action offered ZEROS THAT SHOULD HIDE OR REFRAME "<0 reviews>" -> hide | "New" EMPTY STATES THAT ARE ALREADY GOOD ``` Lead with anything dishonest. That is the only category where the cost keeps growing the longer it stays. ## Rules - Run it empty. Reading the code misses what a screen composes to. - Never pad. Show less, reframe honestly, or explain, but never invent. - Hide zeros that measure your youth; keep zeros that describe the user. - Every empty state gets exactly one obvious next action, and that action must work today. - "No results" without context is a dead end. Say whether the shelf is empty or the query missed. --- From [Toolbay](https://toolbay.ai/product/empty-state-audit). Free to use, modify, and share. Keep this line and others can find it too.