Theme — Asteroid Design System
Asteroid Design System
Component kitchen sink and token reference
Colors
Semantic tokens — OKLCH color space, light & dark mode
Surfaces
- background
- card
- light-muted
- muted
- heavy-muted
Brand
- primary
- primary-light
- secondary
- secondary-light
- accent
Status
- success
- success-light
- warning
- warning-light
- queued
- queued-light
- destructive
- destructive-light
Typography
Two voices — InterVariable for UI, Freight Big Pro for brand & editorial. IBM Plex Mono for coordinates.
Voice 1 · UI · Inter
The operator's console.
Dense UI — tables, forms, buttons, labels. ~95% of the platform and all landing supporting copy.
The quick brown fox jumps over the lazy dog. 1,284 executions completed in 3m 42s average.
Voice 2 · Brand & Editorial · Freight Big Pro
Build agents that run the web.
Landing hero & section titles. In-product hero moments, empty states, onboarding, quotes. One expressive voice across both surfaces.
“A polished power tool that doesn't intimidate.”
Coordinate · IBM Plex Mono
Not a voice — a coordinate system. Identifiers, versions, model slugs, metadata k:v pairs, cron expressions, URLs & paths, raw enum values, and tabular numerics. Where precision beats emotion. Use <Mono> from @workspace/ui/components/mono outside of DataTable (which has meta.mono).
Mono
92890146v3gpt-5claude-opus-4-7-thinking*/5
Not mono
- Acme Corp
- Completed $1,204.50 (stat-card rule wins)
- Created a moment ago
Platform type scale — Inter
- H1 Page Title text-2xl font-semibold tracking-tight
- H2 Section Header text-base font-semibold
- H3 Card Title text-sm font-semibold
- H4 Subsection text-xs font-medium
- Body Default UI text used in sidebar and nav elements text-xs font-normal
- Body-lg Main content body text for longer paragraphs text-sm font-normal
- Caption SIDEBAR LABEL text-[11px] font-medium tracking-wide
- Code exec_a1b2c3d4e5f6 font-mono text-[13px]
Editorial tier — Freight Big Pro (use sparingly)
- Hero Welcome to Asteroid.. type-editorial-hero
- Title No executions yet.. type-editorial-title
- Quote “It just works — and then it keeps working.” type-editorial-quote
In-context example — empty state
Executions
Nothing to see here — yet.
Kick off your first agent run and this page will fill up with the interesting stuff.
Start an agent
Serif headline + Inter supporting copy + mono overline. The three voices playing together.
Buttons
Button face is lifted from flow, shadow at flow position — sinks on hover, presses on active.
Variants × Sizes
| variant \ size | sm | default | lg |
|---|---|---|---|
| default | default | default | default |
| destructive | destructive | destructive | destructive |
| outline | outline | outline | outline |
| ghost | ghost | ghost | ghost |
| link | link | link | link |
With Icons
- New Agent Run
- Execution
- Deploy
- Export
- Filter
- Delete
- Copy ID
- View Docs
Icon Only
States
- Default rest
- Disabled disabled
- Loading loading pattern
Common Patterns
- Dialog Footer — confirm / cancel
- Cancel Confirm
- Destructive Confirmation
- Keep Delete Agent
- Toolbar — mixed actions
- Run Pause
- Tab Toggle — ButtonGroup variant switching
- Active Inactive Inactive
- Page Header — primary + secondary actions
My Agents
- Export New Agent
- Split Button — primary action + dropdown
- Deploy
Iconography
Icons are a supporting voice — they reinforce an affordance or classify a type. Four working sizes, six semantic colors, and a strict anti-pattern list. See DESIGN.md §Iconography for the canon.
Size scale
Four working sizes carry 99% of the system. size-3 is not canonical — it survives as drift. size-6 is reserved for rare page-header chrome.
size-312pxsize-3.514pxsize-416pxsize-520pxsize-624pxsize-832px
In buttons
Button hands every nested icon size-3.5 and gap-1.5. Never pass size-* or mr-* class overrides.
Create Download Refresh Delete Small Large
In tabs
TabsTrigger size="default" and ChromeTab use size-3.5. size="lg" steps up to size-4 because the tab IS the page title.
| size | default |
|---|---|
| lg | Profiles |
In entity and stat cards
Entity full-tile (size-9) holds size-4; compact (size-7) holds size-3.5. Stat card header icon is size-3.5, right-aligned in the muted label row.
Do
- 2h ago Last run
Don't
- 2h ago
Color canon
Icons inherit currentColor. Use semantic tokens. Never text-green-600, text-red-500, and friends.
text-successtext-destructivetext-warningtext-primarytext-admintext-muted-foreground
Empty state hero
EmptyMedia variant="icon" renders a size-10 muted tile around a size-8 icon. Do not pass a size class on the icon itself.
No agents yet.
Create your first agent to start running workloads.
Create agent
Inputs with a leading icon
No built-in SearchInput primitive yet — use an absolute size-3.5 Search icon with muted foreground and pad the input to match.
Anti-patterns
- Active
- Status badge tint IS the signal — one glyph or none.
Button Groups
Grouped actions with shared offset shadow.
Horizontal — outline
Left Center Right
Horizontal — alignment
Vertical
Top Middle Bottom
With Text & Separator
Page 1 of 10
Save
Cancel
Variant groups
Default Group
Destructive Group
Badges
Tinted backgrounds with colored text — quiet labels, not loud fills.
Variants
- Default
- Secondary
- Error
- Outline
Status — filled, distinct from brand colors
- Queued
- Starting
- Running
- Completed
- Paused
- Awaiting
- Failed
- Canceled
Admin Indicators
The affordance IS the signal — admin elements paint themselves amber. Badge stays as the page-header chip and the fallback for elements that already carry color. See DESIGN.md → Admin Indicators.
Token
--admin
Solid amber. Use on raised admin buttons; rarely as a surface.
bg-admin/10Tinted background — strip, hover/active states, ghost surface.
border-admin/30Border tint for the strip and outlined admin elements.
Agents
My org All orgs
Cross-org agents visible — internal toolbar above
Toggle Admin mode for this preview Admin mode
Admin PageGate (toggle-off interstitial)
Admin mode is off
You turned Admin mode off — internal tools are hidden so it's safe to demo. Re-enable to view this page.
Enable Admin mode
Tabs
Chrome-style tabs — active tab uses bg-background against a muted bar surface, with inverted SVG corners.
One Chrome-tab visual
Two component APIs for different semantic models. Both render identically — pick based on what the tabs represent, not how they should look.
Tabs(Radix) — sub-navigation within a page or resource (Overview / Analytics / Reports).ChromeTabs— browser-style document tabs (the agent workspace: builder, executions, open execution tabs).
Default framed look — Tabs / ChromeTabs
The canonical chrome-tab visual: transparent strip, active tab with border + stroked inverted corners, content panel with full border +rounded-b-lg, and one shared drop-shadow over the whole tongue-card silhouette. Both Tabs and ChromeTabs default to framed; the panel border auto-paints so consumers don't repeatborder border-t-0 rounded-b-lg. Suppress the drop-shadow when the strip and workspace can't share a DOM subtree (agent header pattern) via framedShadow={false}.
Tabs framed (sub-navigation, on bg-light-muted)
- Overview Logs Settings
ChromeTabs framed (one shared drop-shadow)
- Builder
- Executions
To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Modals
One shell, four sizes (sm / md / lg / full), five fixed slots: header → optional persistent identity strip → optional tab strip → body → footer. Tabbed modals share the chrome-strip + chrome-tab pattern with §Page shell — a modal-with-tabs is structurally a tiny chrome page. See DESIGN.md §Modals for the canon.
Confirm
sm · 448
One question, one answer. Destructive footer: outline-Cancel + destructive primary with the verb spelled out.
Open confirm
Form
md · 672
Single-purpose form. Header description carries the intent; FieldGroup runs the body; Cancel + Save in the footer.
Open form
Tabbed
lg · 896
The canonical shape: header → persistent identity strip → chrome tabs → body → footer. Mirrors §Page shell tabbed pages.
Open tabbed modal
List-pick
md · 672
Search + scroll + multi-select inside a DialogBody. The toolbar pins to the top, the list scrolls, the count summary lives in the footer.
Open list-pick
Loading + error
md · 672
Same shell, two states. Loading uses skeleton inside the body. Blocking errors are an in-body banner — never a toast.
Open states
Right-action + split footer
lg · 896
The Run Agent shape: Pre-fill Select sits in the header right-action slot; Schedule and Advanced cluster left in the footer; Cancel + Run cluster right.
The four sizes
- sm · sm:max-w-md · 448px — confirms, single-input.
- md · sm:max-w-2xl · 672px — single-purpose forms, list-picks.
- lg · sm:max-w-4xl · 896px — tabbed config, multi-section forms. The default for "complex".
- full · 95vw / 90dvh — workspace views (file manager, diff, debugger). Last resort.
Form Elements
Text Inputs
Agent Name
Search
Disabled
Input + Button combinations
Go
Send Invite
Cards
Content containers with header, body, footer
Agent Performance
Last 7 days execution metrics
1,284 Total executions
View Details
Success Rate
- Completed vs failed
- 94.2% +2.1%
- View Report
Active Agents
- Currently deployed
- 12 of 20 slots
New Agent
Stat cards
Compact metric tiles — label + icon, value, helper. Used on /overview and /executions and reserved for future summary strips (billing, usage, scheduled). One row, 3 or 4 tiles, top of page.
Anatomy
- Executions
- 2,840
- 2,738 completed · 68 failed · 34 canceled
- label sentence case · text-[11px] font-medium text-muted-foreground
- icon size-3.5 · neutral by default; one status accent allowed
- value text-xl font-semibold tabular-nums · always tabular so values align across the strip
- helper breakdown / context · text-xs text-muted-foreground · color words, not the whole tile
- shell gap-1 border py-3 shadow-none · flat, no offset shadow — tiles are inert
Strip — 4 tiles · /overview
- Executions 2,840
- 2,738 completed · 68 failed
In progress
- 7
- 3 held for review
- Outcomes
- booked 412
- held 38
- no-stock 14
Agents
- 12
- 3 updated today · 5 saves
Strip — 3 tiles · /executions
- Executions 412
- 412 completed
In progress
- 0
- nothing live
- Outcomes
- no outcomes yet
Loading — full strip skeleton
Future use — billing strip (same shell, different content)
- Spend $1,284.40 this period · 62% of cap
- Plan Scale
- renews 14 May
- Top agent Insurance Scraper $612.18 · 48% of spend
Entity cards
How a domain entity (Agent Profile, Integration, Notification, API Key, Schedule, …) renders anywhere in the platform. The same identity — icon, name, type, meta — at three densities: full on index pages, compact in side panels and drawers, and chip as an inline reference with a popover. Detail lives in a vertical stack of named body sections — and only those sections — so two cards for different entity types still read as members of the same family.
Anatomy — full variant
Webhook
My Webhook
- URL https://webhook.site/dddddddd
- Version v2
- Headers 0
- Monitoring:
- Execution Completed
- Execution Failed
- Execution Started
Edited 2m ago · Created 15m ago
- Icon tile — neutral muted square. The only place a brand color or product logo is allowed in the header.
- Overline (
EntityCardType) — small uppercased mono label sitting above the title. The icon already telegraphs the type via shape; the overline is the belt-and-braces text label. - Title —
text-base font-semibold, truncating. The dominant element of the header now that identity info is split across overline + title only. - Meta (optional) — supplemental identity line below the title. Only used when the entity has a non-audit fact more identifying than its name (e.g. Schedule's cron description). Most entities skip this entirely.
- Actions — right-aligned dropdown trigger.
- Body — a vertical stack (
space-y-4) of named section primitives. This card usesPropertyList+FilterRow. - Footer — thin top border + small muted line at the bottom. All audit lives here:
EntityCardTimestampswithshowCreatedrendersCreatedalways andEditedwhen changed (60s threshold). Empty footers don't render at all.
Variants — full / compact / chip (hover the chip)
Full
- Webhook
My Webhook
- URL https://webhook.site/dddddddd
- Version v2
- Headers 0
- Monitoring:
- Execution Completed
- Execution Failed
- Execution Started
Edited 2m ago · Created 15m ago
Compact
My Webhook
- version v2
- headers 0
- url webhook.site/dddddddd
Chip + popover
- Webhook · My Webhook
Body grammar & footer — six body sections plus the audit footer
Every full or popover body is a stack of named sections. Picking from this set is mandatory; ad-hoc divs in an entity body are out. Two densities of PropertyList for key/value, four cluster sections for everything else.
PropertyList density="grid"
- URL https://webhook.site/dddddddd
- Version v2
- Headers 0
PropertyList density="inline"
- version v2
- headers 0
- url webhook.site/dddddddd
PillRow
- Features
- Captcha Solver
- Persistent IPC
- Cache
- Ad Blocker
- Popup Blocker
- Popups as Tabs
- Media Blocker
DividedList
- Stored Secrets 3
REPLY
- Login
- MFA Token
- 2FA
- API_KEY
- Secret
DividedList (editable, with actionSlot)
- Stored Secrets 3
REPLY
- Login
- MFA Token
- 2FA
- API_KEY
- Secret
Monitoring: Execution Completed Execution Failed Execution Started +4 more
EmptyRow
- Stored Secrets 0
- No secrets stored
EntityCardFooter (default)
- Edited >60s after create — meta already shows
Created. - Edited 5m ago
EntityCardFooter (showCreated)
- Custom-meta entity (e.g. Schedule) — footer carries
Createdtoo. - Edited 5m ago · Created 2d ago
Composed example — three sections in one body
The same shell with a stacked body. Sections sit at space-y-4; headers and content within a section sit at space-y-2.
- Webhook
Production webhook
- URL https://hooks.example.com/abc
- Version v2
- Headers 3
Chip references — hover or click to expand
Chips are entity references inside other content (table cells, log lines, toasts, breadcrumbs). Identity only — icon, type, name. The popover shows the entity's compact card plus an allow-listed subset of body sections, and a footer link that opens the entity in a new tab.
## Page shells
Two underlying surface modes — canvas (used by Plain, Hero, and Start) and chrome (used by Tabbed and Detail) — produce the platform page shapes. Canvas pages get a bg-light-muted well with bg-card surfaces lifting off it. Chrome pages drop the canvas and let a bg-sidebar strip + tabs sit directly above a single bg-background content panel — the active tab is the only seam between chrome and content. Sidebar carries the page label; the title slot is reserved for the active tab (Tabbed/Detail), the editorial composition (Hero/Start), or nothing at all (Plain).
Anatomy — canvas mode vs chrome mode
- Canvas mode · Plain & Hero
- Overview
- Agents
- API Keys
- Billing
- New key
Chrome mode · Tabbed & Detail
- Overview
- Profiles
- Integrations
- Profiles
- Pools
- Docs
- Create
The core shell shapes
| Page has… | Title slot | Subtitle slot | Examples |
|---|---|---|---|
| Tabs | Drop. Tabs are the identity. | Drop. Use InfoCard inside tab content if needed. |
Agent Profiles, Admin → Models / Usage |
| Editorial hero | Drop. Hero replaces it. | Drop. Hero supporting copy carries it. | Overview |
| Start composition | Drop. Split editorial prompt replaces it. | Drop. Primary composer and rail copy carry the context. | Create / get started |
| Custom resource header | Resource name · dynamic content | Drop. | Agent, Execution, Batch detail pages |
| Plain top-level route, no tabs | Optional. Prefer dropping — sidebar already says it. | Optional. Treat as a smell. | API Keys, Integrations, Manage Users |
Tabs — pick the API by semantic, not by look
- Tabs (Radix) — Internal sub-sections
- ChromeTabs — Browser-like. Tabs are openable, draggable, closable. Outer state owns the list.
Both render the same chrome-tab visual — same surface, same shoulder, same inverted corners on the active tab. The split is about what the tabs mean, not how they look.
Create / Start Surface
The Start page shape: a canvas-first decision surface for creating an agent, onboarding a user, or choosing a first workflow. One editorial composition may span the primary composer and the supporting template rail, but everything below returns to compact Inter UI.