Design System
Foundations · Patterns

Patterns.

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.

Empty states

Every empty state is an onboarding moment. Name what's empty, explain the one next step, and offer it as a button.

First-run empty

No workspaces yet

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

No workspaces yet

Workspaces hold projects, members, and settings. Create one to get started — you can rename it later.

Filtered empty

No results

When the data set isn't empty but the filter doesn't match, tell the user what they filtered and offer to clear it.

No projects match "payments"

Try a different spelling or widen the filter. There are 12 other projects in this workspace.

Loading

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.

Skeleton

Content-shape placeholder

Matches the layout of what's loading. Shimmer runs at constant speed, infinite, paused by prefers-reduced-motion.

Spinner + progress

Indeterminate & determinate

Spinner for < 3s waits. For anything longer, the user needs to see progress.

Importing · 62% · ~12s remaining

Confirmation

Only for destructive, irreversible actions. Name the thing being destroyed in the button label — not just "Delete" but "Delete workspace".

Destructive

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

Delete acme-studio?

This will permanently remove 24 projects and revoke access for 9 members. You can't undo this.

Forms

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.

Default

Create workspace

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.

Create workspace
Letters, numbers, and dashes. Shown in URLs.
Error state

Validation inline

Error text replaces the hint. The input border goes danger. On submit, focus moves to the first invalid field.

Create workspace
Spaces aren't allowed. Try acme-studio.

Feedback

Confirm, warn, or error — three voices, three surfaces. Banners stay until dismissed; toasts auto-hide; inline errors live inside forms.

Success

Toast + banner

Toasts: bottom-center, auto-dismiss ~2.5s. Banners: top of the affected view, user-dismissed.

Workspace created
Error

Something went wrong

Name what failed, then the recovery. Never blame the user — even if they did the wrong thing.