# Desktop Design System Conventions for the Electron desktop app (`apps/desktop`). Read this before adding a component, overlay, or style. The rule of thumb: **one source per concern, tokens over literals, flat over boxed.** If you reach for a raw color, a one-off shadow, a bespoke button, or a hardcoded `px-*` on a control — stop, there's already a primitive for it. This file owns the visual and interaction contract. Read [`AGENTS.md`](./AGENTS.md) for architecture, state, resolver, transport, and testing rules. This doc contains two kinds of content, maintained differently: - **Principles** (flatness, intent, feedback, motion, cancellation) are durable. They hold as components come and go. - **Named contracts** (tokens, `Button` variants, primitive names) are the design system's current API. They are maintained *with* the code: if you change a primitive, token, or variant, update its entry here **in the same change** — a stale name in this file is a bug, exactly like a stale type. When a rule and the code disagree, fix whichever is wrong rather than forking a one-off at the call site. ## Principles 1. **Flat, not boxed.** No card-in-card, no divider borders inside a panel. Group with whitespace and a single hairline, never nested rounded boxes. 2. **Borderless elevation for floating panels.** Overlays float on `shadow-nous` + a `--stroke-nous` hairline, not thick framed boxes. In-panel structure may use token hairlines sparingly. 3. **One primitive per concern.** One `Button`, one set of control variants, one `SearchField`, one `Loader`, one `ErrorState`. Migrate onto them; don't fork. 4. **Tokens, not literals.** Reference CSS vars (`--ui-*`, `--shadow-nous`, `--theme-*`), never raw hex / ad-hoc rgba in components. 5. **Style lives in the primitive.** Variants and sizes own padding, radius, color, chrome. Call sites pass a `variant`/`size`, not `className` overrides that re-specify those. 6. **Intent before automation.** Surface useful actions and previews, but do not open panes, move focus, or navigate because a tool happened to produce something. 7. **Immediate feedback.** Direct manipulation updates the view first. Network or disk persistence reconciles afterward and rolls back visibly on failure. ## Information architecture - **Chat is the home surface.** The transcript and composer stay primary; tools, previews, files, review, and terminal complement the conversation. - **Pages are durable destinations.** Chat, Skills, Messaging, and Artifacts remain in shell chrome. Do not hide a distinct product noun inside an unrelated page. - **Route overlays are short tasks.** Settings, Command Center, Cron, Profiles, Agents, and Starmap render as `OverlayView` cards and return to the previous route on close. Model/session pickers and dialogs layer above the current surface; they are not navigation stacks. - **Panes are working context.** Preview, files, review, and terminal remain attached to the current task. Their state survives temporary hiding and chat switches where the underlying tool is meant to persist. - **One action, one home.** A command may have keyboard, palette, and visible affordances, but they invoke the same action and state. Do not fork behavior per entry point. - **Projects own workspace cwd.** Use Sidebar → Projects for local folders and worktrees; do not reintroduce a per-session/right-sidebar folder-picker flow. Navigation must preserve context. A background session finishing, a tool result arriving, or a project refresh may update badges and cached data; it must not replace the foreground transcript or steal focus. ## Surfaces & elevation Floating panels (base `Dialog`, route overlays, boot/install/update surfaces, model-picker, onboarding, prompt overlays, notifications) use: ``` shadow-nous /* downward-weighted, layered contact→ambient falloff */ border-(--stroke-nous) /* currentColor hairline, theme-adaptive */ ``` Both are CSS vars in `src/styles.css` — tune in one place, everything inherits. Don't add per-overlay `shadow-[…]` or `border-(--ui-stroke-secondary)` one-offs; if elevation needs to change, change the token. Menus and popovers use their own shared `shadow-md` + `--ui-stroke-secondary` primitive treatment. Drag affordances may use tokenized dashed targets and local blur. These are semantic surface classes, not licenses for call-site shadow or border inventions. ## Stroke & color tokens | Token | Use | | --- | --- | | `--ui-stroke-primary…quaternary` | hairlines, in descending strength | | `--ui-stroke-tertiary` | the default in-panel divider / list hairline | | `--stroke-nous` | the overlay hairline (pairs with `shadow-nous`) | | `--ui-text-primary / -secondary / -tertiary` | text hierarchy | | `--ui-bg-quaternary` | soft control fill (secondary button) | | `--chrome-action-hover` | hover fill for quiet controls | | `--theme-primary`, `--ui-accent` | brand/accent | Never hardcode `border-gray-*`, `bg-white`, `text-black`, etc. The white tile in `BrandMark` is the one sanctioned literal (the mark needs a fixed backdrop). ## Buttons — one component `src/components/ui/button.tsx` is the single source. Pick a `variant` + `size`; do **not** pass `h-*`, `px-*`, `py-*`, or icon-size overrides. **Variants:** `default` (primary), `destructive`, `secondary` (soft fill — the default non-primary look), `outline` (transparent + 1px inset ring, no fill/shadow), `ghost`, `link`, `text` (boxless quiet inline — "Cancel", "Clear"), `textStrong` (bold underlined inline affordance — "Change", "Open logs"). **Sizes:** `default`, `xs`, `sm`, `lg`, `inline` (flush, zero box — for buttons that sit inside a heading/sentence; replaces `h-auto px-0 py-0`), `micro` (status-stack/table-footers), and the icon family `icon` / `icon-xs` / `icon-sm` / `icon-lg` / `icon-titlebar`. **Icon-only buttons must have a tooltip.** Every button with an `icon*` size carries no visible text label, so it must be wrapped in `` with a descriptive label (matching the button's `aria-label`). Never use the native HTML `title=` attribute — it's unstyled, delayed (~500ms OS default), and visually inconsistent with the instant themed `Tip`. An enforcement test (`src/components/ui/__tests__/no-native-title.test.ts`) fails on any `