Recipes, not ingredients. These are the composed moments the product hits over and over — an empty list, a loading state, a destructive confirmation, a form with an error. Each is built from tokens and components only; no bespoke CSS.
Every empty state is an onboarding moment. Name what's empty, explain the one next step, and offer it as a button.
Centered, small-icon, one primary action. Reserved for the first time a user hits a zero-data view — not for filtered results.
icon 56 · heading 17 · body 13.5 · gap 10
Workspaces hold projects, members, and settings. Create one to get started — you can rename it later.
When the data set isn't empty but the filter doesn't match, tell the user what they filtered and offer to clear it.
Try a different spelling or widen the filter. There are 12 other projects in this workspace.
Pick by intent. Skeleton when we know the shape of what's coming. Spinner for indeterminate short waits. Progress when we can measure — uploads, imports, exports.
Matches the layout of what's loading. Shimmer runs at constant speed, infinite, paused by prefers-reduced-motion.
Spinner for < 3s waits. For anything longer, the user needs to see progress.
Only for destructive, irreversible actions. Name the thing being destroyed in the button label — not just "Delete" but "Delete workspace".
Icon is danger-tinted. The primary button carries the destructive color. Cancel is always first in keyboard order.
shadow-lg · 400px width · gap 14
This will permanently remove 24 projects and revoke access for 9 members. You can't undo this.
One field per row, labels above, hints below. Errors replace hints with the same vertical space so nothing jumps. Primary on the right, cancel/ghost on the left.
Every field has a label, not a placeholder-as-label. Hints sit below the input in ink-subtle. The submit button says exactly what it'll do.
Error text replaces the hint. The input border goes danger. On submit, focus moves to the first invalid field.
Confirm, warn, or error — three voices, three surfaces. Banners stay until dismissed; toasts auto-hide; inline errors live inside forms.
Toasts: bottom-center, auto-dismiss ~2.5s. Banners: top of the affected view, user-dismissed.
Name what failed, then the recovery. Never blame the user — even if they did the wrong thing.