1.0.5.3 Empty-state & error-state patterns (reusable components)
Estimate: 12m · Depends on: 1.0.5.2
Every screen in Motir will eventually have an empty state ("you don't have any projects yet — create your first one") and at least one error state ("we couldn't load this — try again"). If each screen invents its own version, the product feels inconsistent. This Subtask produces two reusable React components — EmptyState and ErrorState — that every screen composes when it needs them.
Why these are separate from the primitives: primitives (1.0.5.2) are atoms — Button, Input, Card. Empty / error states are compositions — they use a primitive (Card) plus an icon, headline, body text, and a CTA. They're closer to "patterns" than "atoms." Keeping them in their own Subtask gives them the focused design attention they deserve (they're the screens users see when something's wrong; the polish bar is high).
What you'll do: Create /components/ui/EmptyState.tsx and /components/ui/ErrorState.tsx. Each takes title, description, optional icon, optional action (a Button or button-shaped link), and renders inside a Card. Add minimum-viable illustrations — even a single nice SVG icon per state is enough for v1. Add them to the specimen page from 1.0.5.2 with at least 2 sample uses each.
Acceptance criteria
<EmptyState title description? icon? action? />renders a centered, padded card with an icon (default: a friendly placeholder SVG), a title (h2-equivalent), an optional description (muted text), and an optional CTA (typically a Button).<ErrorState title description? error? retry? />renders similarly but with a warning/error visual treatment.errorcan be anErrorobject whose message is shown when in dev mode;retryis an optional callback that shows a "Try again" button.- Both use only primitives from 1.0.5.2 — no new component patterns introduced.
- Specimen page renders 2 sample uses of each (e.g., "no projects yet" empty + "couldn't load workspace" error + "no comments" empty + "PR webhook failed" error).
- Accessible: error state has
role="alert"; both have semantic heading.
Context refs
/components/ui/Card.tsx,Button.tsx(from 1.0.5.2) — the building blocks/app/_tokens/page.tsx(from 1.0.5.1, extended in 1.0.5.2) — to add specimens to- Empty-state UX patterns reference (Vercel / Linear / Stripe examples — fetched at prompt-gen)