web/components/ui

Buttons

The bezel Button — the app's unified interactive primitive. `primary` is reserved for the dominant affordance on a surface (at most one per region); `ghost` is the header/panel default; IconButton is the icon-only form.

Variants
States
kit/components

Rail navigation

Compact destinations shared by Zo app rails. Icons stay regular at rest and switch to their duotone twin only for the active destination.

kit/components

Segmented control

Mutually exclusive options with a flat selected fill and border. Options can include a leading icon.

Icon + label
Sizes
view: list · range: week
kit/components

Badges

Badge is the larger pill chip for cards and tabs; StatusBadge is the tiny inline pill for row-level state (Public, Connected, Installed, …).

Badge
DefaultSecondaryOutlineDestructive
StatusBadge tones
neutralaccentaccentSolidwarningsuccesscaution
kit/components

Persona avatars

One avatar path for uploaded images, the built-in Zo Pegasus, and custom-persona initials. Image failures fall back without shifting the surrounding row.

Default Zo
RCustom persona
Uploaded personaUploaded image
kit/patterns

Activity time

One live timestamp policy for activity surfaces: seconds, minutes, hours, and days ago, then abbreviated month and day after a week.

kit/patterns

Date and time

One formatter for absolute moments: the reader's locale, an explicit zone with its label, and a date-only style. DateTime shows the relative label and opens the absolute value on hover, keyboard focus, and tap. This demo pins the locale to en-US so the server and browser agree.

formatDateTime
zone: "UTC"
Oct 6, 2026, 1:50 PM UTC
zone: "UTC", style: "date"
Oct 6, 2026 UTC
locale: "de-DE"
6. Okt. 2026, 13:50 UTC
DateTime
kit/tokens

Status color recipes

The semantic status vocabulary — success, warning, info, and destructive (error). The tokens are mode-aware, so never pair them with dark: variants; raw palette classes (text-emerald-600, …) are banned by the local/no-raw-palette ESLint rule. Three recipes: plain text/icon, tinted chip, solid fill.

Text — text-{token}
Deployed successfullyCertificate expiringUpdate availableBuild failed
Tinted chip — bg-{token}/10 text-{token} border-{token}/30
successwarninginfodestructive
Solid fill — bg-{token} text-{token}-foreground
successwarninginfodestructive
web/components/ui

Tooltips

Radix tooltip with the app's delays and elevation; keyboardHint renders the shortcut chips inline. The first one is pinned open for reference — hover the second.

kit/components

Context menus

The right-click menu — same item anatomy as the dropdown menu, anchored to the pointer. Right-click (or long-press) the surface below.

Right-click here
last action: —
kit/components

Popovers

The click-to-open anchored panel behind filters, pickers, and inline help. Modal on touch devices; dismisses on outside press or Esc.

kit/components

Modals

Dialog is the general modal shell (scrolling body, built-in close button); AlertDialog is the confirm shape — its footer pairs an outline cancel with a primary action on the same bezel chassis.

kit/components

Drawers

The sheet used as the mobile stand-in for popovers and dialogs; slides from a viewport edge with a drag handle on the bottom variant.

web/components/ui · kit/components

Indicators

Progress for determinate work; KeyHint renders keyboard shortcuts as key chips in the regular UI face (mono glyphs are hard to read).

Progress
x
x
KeyHint
⌘K⌘Shift.CtrlShiftP
kit/components

Form fields

Input and Textarea use one flat field boundary and a visible focus ring. Textarea grows with its content inside a ScrollArea; invalid fields show a destructive border.

Input
Textarea
kit/components

Code editor

A line-numbered textarea over a Shiki-highlighted mirror. Tab and Shift+Tab indent and outdent; Enter keeps the line's indentation; Escape, then Tab, leaves the field. `readOnly` shows the same view without editing; `wrap` soft-wraps prose.

Python
Markdown, wrapped
Read-only
kit/components

Selects

A single-choice picker with a flat trigger. Pass `items` when labels differ from values; the closed trigger renders the mapped label.

value: gpt
kit/components

Comboboxes

A single-choice picker you can type in. Use it for people and resources. A short fixed set stays a select.

value: none
kit/components

Selection controls

Checkbox, Switch, and RadioGroup use flat surfaces with explicit checked states. Label identifies each field; Separator divides the groups.

Checkbox
Switch
notifications: on
Radio group
kit/components

Sliders

The range slider used by the video player's volume control; thumbTooltipLabel shows a hover hint above the thumb.

Volume
Volume 65%
value: 65
kit/components

Toggle groups

Single-select ToggleGroup — the connected-segments pattern (service create panel's HTTP/TCP/Process picker). The pressed item takes the accent paint.

Outline, single select
protocol: http
kit/components

Tabs

A flat tab strip with a tinted active tab and fine border. Arrow keys move between tabs; panels swap below.

1,204 requests this cycle.
tab: requests
kit/components

Tables

The data table used by billing, usage, and the SQLite viewer. Borders live on the cells (not rows) so they survive sticky headers; the container scrolls horizontally on overflow.

InvoiceDateAmountStatus
INV-0041Jun 01, 2026$20.00Paid
INV-0040May 01, 2026$20.00Paid
INV-0039Apr 01, 2026$18.50Refunded
kit/components

Collapsible

The disclosure primitive behind settings sections, thinking blocks, and tool-call groups in chat. The chevron rotates with the open state; the panel height animates via the global collapsible keyframes.

Retries: 3 · Timeout: 30s · Region: auto

These options only apply to scheduled runs.

open
kit/components

Accordion

Base UI disclosures with unique accessible labels and standard keyboard behavior. Opening one closes the rest unless multiple panels are enabled.

Your files, agents, and sites — one computer in the cloud.

kit/components

Command

The cmdk-backed command palette list: fuzzy input, grouped items, empty state. CommandDialog wraps it in the kit Dialog for the ⌘K pattern.

kit/components

Calendar

react-day-picker themed to the token system; nav chevrons and day cells reuse the flat-button variants. Single, range, and multi-select come from DayPicker's own mode prop.

November 2026
kit/components

Resizable

react-resizable-panels wrappers: Group + Panel + Separator (withHandle shows the grip). Web's workspace split panes are built on these.

Sidebar
Content
kit/components/file-tree

File tree

The @pierre/trees shell behind web's Host Files tree and the workspace Knowledge tree: FileTreeView takes a loading, error or ready state and builds a deterministic model from the ready nodes through createTreeInput. Deep-import only; fileTreeThemeStyle maps Pierre's --trees-theme-* variables onto kit's --card, --foreground, --accent, --border, --ring and --input tokens, so the tree follows the app theme and dark mode with no extra CSS.

Ready
Loading
Error
kit/components/flat-button

Flat button

The shadcn-shaped CVA button that predates the bezel system — variant/size/rounded recipes plus a built-in loading spinner. Deep-import only: the kit barrel's Button is the bezel system's. Prefer the bezel Button for new surfaces.

Variants
States
kit/components

Hover cards

The rich-preview popover used for persona cards, usage summaries, and credit alerts. Opens on hover after a short delay; content portals above everything else.

kit/components

Searchable list

SearchableListContainer pairs the app's SearchInput with a fixed-height ScrollArea viewport — the search/filter/scroll pattern used by pickers and settings lists. Esc clears; zero matches hand off to the shared EmptyState.

web/components/layout/collection

Empty states

Collection.EmptyState is the shared shape for the “no rows right now” slot. Three kinds: intro (first-run, invitational CTA), no-results (a filter matched nothing), and error (load failed, retry).

No chats yet

Start a conversation and it shows up here.

No matches

Nothing matches the current filter.

Couldn't load files

Something went wrong on our end.

www/components/docs

Code block

The docs code block: card chrome, a horizontal ScrollArea, and a hover copy button. The fenced code arrives pre-highlighted by Shiki (in the docs via @shikijs/rehype at build time, here via codeToHast on the server).

Unified diff

kit/markdown

Markdown

The read-only Markdown viewer: GFM in the typeset styles, with a leading YAML front matter block shown as fields above the body and Mermaid fences drawn as diagrams. Raw HTML and images never render, and links open in a new tab.

title

Launch plan

status

draft

tags

research, decisions

Launch plan

Your agent keeps this plan current as decisions land. See the guide.

StepOwner
ResearchZo
DecideYou
zo deploy --stage preview
graph LR Research --> Decide --> Ship
kit/components

Surfaces

RaisedSurface is reserved for content cards and callouts. Buttons, selectors, icon tiles, and tabs use flat treatments.

muted
background
subtle
accent
kit/components

Skeleton

The pulsing placeholder block for loading states — size it with width/height utilities to match the content it stands in for.

kit/components

Spinners & shimmer

CharSpinner cycles braille glyphs for loading states (the `z` variant is the app default); TextShimmer sweeps a highlight across streaming or pending labels.

CharSpinner variants
⠃z⣿opus⢕rune⢕spell
TextShimmerThinking about it…
www/components/ui

Chips

BrandedChip is the inline link chip used in marketing prose — brand-tinted for Discord and GitHub, neutral otherwise.

DiscordGitHubNeutral chip
kit/components

Visually hidden

VisuallyHidden keeps text in the DOM for screen readers and SEO while a decorative element supplies the visible rendering. It hides with inline styles instead of the sr-only utility, so hidden duplicates of visible content can never leak into view if utility CSS goes missing (see the Tailwind CSS generation gotcha in www's AGENTS.md). Use it whenever the hidden text has a visible twin — an icon label, a video-filled word, an SEO copy of on-screen content.

Icon with a hidden label
SitesThe icon is the visible rendering; “Sites” is in the accessibility tree but never paints.