Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

TUICommander — Visual Style Guide

Reference for all UI/CSS/layout work. Every visual change MUST follow this guide.

Design Philosophy

VS Code Dark theme adapted for a terminal-first, developer-focused interface. The UI is a frame for terminal content — chrome recedes, content dominates. No bright whites. Muted UI elements, vivid status colors. Everything monospace except UI labels.

Application Layout

┌─────────────────────────────────────────────────────────────────────┐
│ #toolbar (38px macOS / 32px Win+Linux, --bg-primary, drag region)  │
│ ┌──────────────┬────────────────────────────────────┬────────────┐ │
│ │ toolbar-left │ toolbar-center (tab bar)            │toolbar-right│ │
│ │ (sidebar w)  │ [Tab1] [Tab2] [Tab3] [+]           │ [IDE btns] │ │
│ └──────────────┴────────────────────────────────────┴────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ #app-body (flex: 1, flex-direction: row)                           │
│ ┌──────────┬─────────────────────────────────┬───────────────────┐ │
│ │ #sidebar │ #main                            │ Side panels      │ │
│ │ 300px    │ (flex: 1)                        │ (400px, optional)│ │
│ │ --bg-    │                                  │                  │ │
│ │ secondary│ ┌──────────────────────────────┐ │ ┌──────────────┐ │ │
│ │          │ │ #terminal-container          │ │ │ Diff or      │ │ │
│ │ REPOS    │ │ (flex: 1, --bg-primary)      │ │ │ Markdown or  │ │ │
│ │  section │ │                              │ │ │ Notes/Ideas  │ │ │
│ │  title   │ │  terminal fills this          │ │ │ panel        │ │ │
│ │  repo    │ │  entire area                 │ │ │              │ │ │
│ │   header │ │                              │ │ │ panel-header │ │ │
│ │   branch │ │                              │ │ │ panel-content│ │ │
│ │   branch │ │                              │ │ │              │ │ │
│ │          │ │                              │ │ └──────────────┘ │ │
│ │ FOOTER   │ │                              │ │                  │ │
│ │ [+ Add]  │ └──────────────────────────────┘ │                  │ │
│ │ [icons]  │                                  │                  │ │
│ └──────────┴─────────────────────────────────┴───────────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ #status-bar (28px, --bg-secondary, border-top)                     │
│ [zoom][sessions]  [branch ↑2][PR #42][CI ✓]  [toggles][💡][⚙][?] │
└─────────────────────────────────────────────────────────────────────┘

Key structural rules:

  • #app is flex-direction: column, fills 100vh × 100vw.
  • #app-body is flex-direction: row, flex: 1, min-height: 0.
  • Sidebar is fixed-width (resizable 200–500px), main area fills remaining space.
  • Side panels (Diff, Markdown, Notes) appear right of #main, width 400px, max 50vw.
  • All sections have overflow: hidden — scrolling is on inner content areas only.
  • Status bar is always at the bottom, never scrolls.

Color Palette

Values shown are the :root defaults of global.css (the former VS Code Dark palette, in effect until a theme loads). The app supports 13 bundled themes (Commander is the default and the fallback) — all core colors are CSS custom properties overridden at runtime by applyAppTheme() in themes.ts, which also sets html[data-theme="<key>"] so a theme can carry typography rules in global.css (only clean does: neutral gray --tab-*-rgb tints so typed tabs do not each bring their own hue). Font smoothing is global: html forces -webkit-font-smoothing: antialiased and 0.01em tracking, the rasterization every native-looking macOS app uses. When writing CSS, always use variables, never hardcode core palette values.

Theme gallery (dev only). With make dev running, open http://127.0.0.1:1421/theme-gallery.html (src/dev/themeGallery.tsx). It renders one row per bundled theme: the shipped theme on the left and, on the right, the edit proposed in src/dev/themeProposals.ts (a partial override merged over the JSON; empty when nothing is under review). Each card mounts the real Toolbar, Sidebar, TabBar and StatusBar components over stores seeded once in the page (a fake __TAURI_INTERNALS__ in theme-gallery.html answers every invoke), so a component or stylesheet edit repaints every theme through HMR and a theme JSON edit shows up within a second. Only the terminal body and the dialog are stand-ins; never hand-copy component markup into the gallery, a copy drifts within a session (the page polls /__tuic_themes, a serve-only Vite middleware in vite.config.ts). Use it to check a palette change against every theme at once before touching the live app. A static snapshot of the 2026-09-22 review, taken before its edits were applied, is in docs/design/theme-gallery-2026-09-22/. It is not a build input and never ships. To show it as a TUIC ui tab inside a dev build, use http://localhost:1421/...: the dev app is served from 127.0.0.1:1421, and PluginPanel blanks a same-origin iframe (guardSameOriginNav).

CSS Variables (:root in global.css)

VariableDefault (vscode-dark)Usage
--bg-primary#1e1e1eMain canvas — terminals, panel bodies
--bg-secondary#252526Sidebar, tab bar, status bar
--bg-tertiary#2d2d30Inputs, settings rows, button defaults
--bg-highlight#37373dHover states, active branch bg
--fg-primary#ccccccPrimary text (max brightness for text)
--fg-secondary#a0a0a0Labels, secondary text
--fg-muted#9aa1a9Section titles, tertiary text
--accent#59a8ddPrimary actions, active indicators, links (theme-dependent)
--accent-hover#7abde5Hover on accent elements (theme-dependent)
--activity#59a8ddBusy/activity pulse indicators (fixed in global.css, not overridden by themes)
--success#4ec9b0Positive states, open PRs (teal)
--warning#dcdcaaCaution, pending, main branch icon (yellow)
--attention#e8984cActionable alerts, confirmation prompts (orange)
--error#f48771Errors, failures, closed PRs (coral)
--merged#a371f7PR merged badge (purple)
--unseen#c084fcTerminal completed while user wasn’t viewing (purple, clears on view)
--diff-del--error mixed 25% with whiteDeleted-line counts in the sidebar stats badge. Lighter than --error so a negative diff does not read as an alarm; vscode-light resets it to --error
--border#3e3e42All borders and dividers
--text-on-accent#000000Black text on colored badge backgrounds
--text-on-error#000000Black text on error backgrounds
--text-on-success#000000Black text on success backgrounds

Extended Palette (hardcoded, contextual only)

ColorContext
#d29922Changes requested / review required (orange)
#e3b341CI pending (golden)
#ffd700Rate limit, question icon (gold)
rgba(122, 162, 247, *)Branch ahead/behind tint, pulse glow
rgba(158, 206, 106, *)Diff additions bg, CI success tint
rgba(247, 118, 142, *)Diff deletions bg, CI failure tint

Background Stacking Order (darkest → lightest)

#1e1e1e  --bg-primary    Terminal canvas, main area
#252526  --bg-secondary   Sidebar, tab bar, status bar, modals
#2d2d30  --bg-tertiary    Buttons, inputs, settings rows, panel headers
#37373d  --bg-highlight   Hover, active branch, selected items

Every surface uses exactly one of these four levels. Elevation = lighter.

Typography

VariableStackUsage
--font-monoJetBrains Mono, Fira Code, Hack, Cascadia Code, Source Code Pro, DejaVu Sans Mono, monospaceTerminals, branch names, stats badges, PR badges, code
--font-ui-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Liberation Sans, sans-serifUI labels, buttons, headings, descriptions, settings

Size Scale

VariableSizeWhere
--font-3xs8pxMicro labels, pixel-level detail
--font-2xs10pxSmallest visible labels
--font-xs11pxBadge text, hotkey hints, metadata
--font-sm12pxSection titles (REPOS), secondary labels
--font-md13pxBranch names, tab names, settings labels — default for UI
--font-base14pxBody text, document default
--font-lg15pxPanel headings
--font-xl17pxDialog titles
--font-2xl20pxLarge headings
--font-3xl24pxHero text, splash screens

Font weight: 400 normal, 500 medium (branch names), 600 semibold (repo names, badges), 700 bold (headings only).

Spacing

Fixed Dimensions

VariableValue
--sidebar-width300px (resizable: min 200px, max 500px)
--toolbar-height38px macOS / 32px Win+Linux
--tab-bar-height32px
--status-height28px

Spacing Scale

SizeUsage
1–2pxBranch item vertical margin, micro separation
4pxSidebar content top padding, compact flex gaps, micro padding
6pxIcon-to-text gaps, sidebar footer gaps, repo header padding
8pxButton padding, form gaps, sidebar footer padding, standard gap
12pxBranch item horizontal padding, panel header padding, medium padding
16pxSidebar section margin, branch list left indent, modal padding
20pxDialog content padding, sidebar empty state padding

Use gap on flex containers, not margins between children.

Border Radius

VariableValueUsage
--radius-xs2pxMinimal — focus rings
--radius-sm3pxSmall interactive elements
--radius-md4pxStandard — buttons, badges, inputs, branch items
--radius-lg6pxLarger controls — dropdowns, add-repo button, form inputs
--radius-xl8pxModals, panels, dialogs
--radius-pill12pxPR badges, status pills
--radius-full50%Circles — toggle thumbs, repo initials avatar

Shadows

VariableValueUsage
--shadow-popup0 8px 32px rgba(0,0,0,0.4)Modals, dialogs
--shadow-dropdown0 4px 16px rgba(0,0,0,0.3)Menus, popovers, context menus
--shadow-bottom-anchor0 -4px 20px rgba(0,0,0,0.4)Bottom-anchored panels

Three levels only. Never invent new shadow values.

Transitions & Animation

Durations

DurationUsage
0.1sHover backgrounds, active states — instant feedback
0.15sStandard — opacity, color, transform, border changes
0.2sLayout — sidebar collapse, toggle switches, chevron rotation

Keyframe Animations

pulse-opacity: Opacity 0.4 → 1.0 → 0.4, infinite. Duration 1.5s or 2s. Defined in each CSS Module file that uses it (not in global.css — CSS Modules scope animation names). Used for: active branch icon, CI pending badge, rate limit indicator.

pulse-question: Box-shadow 0 → 0 0 12px 4px rgba(122,162,247,0.4) → 0. Variants exist with red (error) and orange (confirm) colors. Used for: terminal tab glow when agent awaits input.

pendulum: Translates text from 0 to -overflow-px and back. Duration computed dynamically from overflow width (~50px/s, minimum 4s cycle). Uses CSS custom properties --overflow-px and --ticker-duration. Used for: status bar notification text that overflows its container.

Tab status dot color scheme (single ● indicator left of tab name):

  • Grey (opacity 0.3): idle — no session or command never ran
  • Blue (--activity, pulse infinite): busy — producing output now
  • Green (--success): done — command completed
  • Purple (--unseen, static): completed while user wasn’t viewing (clears on view)
  • Orange (--attention, pulse infinite): agent needs user input (question)
  • Red (--error, pulse infinite): API error or agent stuck

Tab type color scheme (gradient background + colored border-bottom):

  • Red (#ef4444): diff tabs
  • Blue (--accent / #7aa2f7): editor tabs
  • Teal (#2dd4bf): markdown tabs
  • Purple (#a78bfa): panel tabs
  • Amber (#fbbf24): remote PTY sessions (created via HTTP/MCP)

Always use ease timing. Respect prefers-reduced-motion. Never transition: all.

Component Reference

#sidebar {
  width: var(--sidebar-width);    /* 300px */
  min-width: 200px;
  max-width: 500px;
  background: var(--bg-secondary);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
}

Section title (e.g. “REPOS”):

  • font-size: --font-sm, text-transform: uppercase, color: --fg-muted
  • letter-spacing: 0.05em, padding: 4px 16px

Repo header:

  • Flex row, gap: 6px, padding: 6px 12px 3px
  • Repo initials: 28×28px circle, --accent bg, --text-on-accent text, --font-xs, semibold
  • Repo name: --font-sm, semibold, uppercase, --fg-secondary, truncated with ellipsis
  • Chevron: the shared ChevronIcon (src/components/ui/ChevronIcon.tsx, 12px SVG), --fg-secondary, rotates 0→90° on expand (150ms ease). Branch rows use a separate, always-visible disclosure button beside the status icon. The collapsed count stays beside the chevron; the status icon and row only select the branch.
  • Actions (⋯, +): hidden by default (opacity: 0), shown on repo-header hover

Branch item (the most complex sidebar element):

.branch-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  border-radius: var(--radius-md);
  margin: 1px 0;
  transition: background 0.1s;
}
.branch-item:hover { background: var(--bg-highlight); }
.branch-item.active {
  background: var(--bg-highlight);
  border-left: 2px solid var(--accent);
  padding-left: 10px;  /* compensate for border */
}

Branch item anatomy (left to right):

[icon 14px + agent count?] [name flex:1] [unmerged mark?] [PR marker?] [diff numbers?] [actions on hover]
  • Icon (18px wide, centered): ★ yellow for main, Y muted for feature, Y accent+pulse when agent active, Y green when shell idle, ? warning (orange)+pulse when awaiting input
  • Name: --font-md, weight 500, --fg-primary, ellipsis on overflow
  • Agent count (when the tab tree is on): a bare --font-3xs digit at the icon’s bottom-right corner with a --bg-secondary halo. Never a pill: a pill covers the glyph that tells main, branch and worktree apart
  • PR marker (optional): a 7px square plus #N, both in the state color, no fill or border. A conflict rotates the square into a diamond so it does not rely on color. Draft and Review outline the square instead of filling it, as their pills did (Review vs Changes requested differ only by fill). Motion matches the pill form: the conflict marker pulses at 1.5s, Checking and CI Running at 2s; the number never moves. The UI Legend renders this same component The state word (Review, Conflicts, …) and any uncommitted-file count live in the data-tooltip
  • Unmerged mark (optional): a 7px outlined square in --fg-secondary, centered in a 14px target with no pill background. It marks a worktree with commits outside the default branch even when its working tree is clean; hover or focus explains why branch deletion is blocked.
  • Diff numbers (optional): plain +N -N in --font-2xs, --success / --diff-del, no chip. Always shown beside the PR marker, never alternating with it
  • Actions (on hover only): max-width: 0 → 44px, two 20×20px buttons (+, ×)

Tab Bar

Located inside #toolbar, center section. Background matches toolbar (--bg-primary).

.tab {
  height: var(--tab-bar-height);  /* 32px */
  padding: 0 12px;
  font-size: var(--font-md);
  font-family: var(--font-mono);
  color: var(--fg-secondary);
  background: transparent;
  border: none;
  border-top: 2px solid transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 80px;
  max-width: 200px;
}
.tab.active {
  color: var(--fg-primary);
  border-top-color: var(--accent);
  background: var(--bg-secondary);
}
.tab:hover:not(.active) {
  color: var(--fg-primary);
  background: var(--bg-tertiary);
}

Tab anatomy: [agent badge?] [name, truncated] [close × on hover]

  • Agent badge: small colored prefix (e.g. C claude, G gemini)
  • Close button: invisible by default, opacity: 1 on tab hover
  • New tab button [+]: 28px circle, --accent color

Status Bar

.bar {
  height: var(--status-height);   /* 28px */
  min-height: var(--status-height);
  background: var(--bg-secondary);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-size: var(--font-sm);
  gap: 8px;
  overflow: hidden;
}

Three sections: left (zoom, status info, CWD, agent badge, ticker), center (PR + CI badges), right (toggle buttons).

Agent badge (.agentBadge): --font-mono, --font-sm, weight 500, 1px 6px padding, --radius-sm, --bg-tertiary bg. Usage-dependent color classes:

  • .agentUsage — --fg-secondary text (normal usage)
  • .agentUsageWarning — #dcdcaa text (usage >=70%)
  • .agentUsageCritical — #f48771 text + pulse-opacity animation (usage >=90%)
  • .agentRateLimited — #f44747 text + pulse-opacity animation

Ticker message (.tickerMessage): --font-mono, --font-sm, --fg-muted, max-width 300px, --radius-sm. Warning priority (>=80): #ffd700 text, gold bg at 0.1 alpha. .tickerClickable adds cursor pointer and hover effect.

Pendulum overflow (.infoTickerActive): When the status info text is wider than its container, a CSS pendulum keyframe animation scrolls the text left then back. Duration is computed dynamically from overflow width (~50px/s). Click dismisses.

Count badges (CountBadge in components/ui): the one counter for every icon button: status bar toggles (errors, ideas, git changes) and the sidebar footer (parked, stale, tunnels). 12px high, --radius-sm, 9px bold tabular digits, top-right corner at -3px of a position: relative button. Color is a tone (accent, error, success), never an inline style. It renders nothing for a zero count.

PR badges (center section): PrBadge + CiBadge components in .githubStatus, separated by a left border. CLOSED PRs are hidden; MERGED PRs have a 5-minute activity-based grace period.

Toggle buttons (right section): --bg-tertiary bg, --border border, --font-xs, 2px 8px padding. Active: --accent bg, white text. Each has a hotkey hint overlay positioned below.

Side Panels (Diff, Markdown, Notes/Ideas)

All follow the same structure:

.panel {
  width: 400px;
  min-width: 300px;
  max-width: 50vw;
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  background: var(--bg-primary);
}
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg-tertiary);
  border-bottom: 1px solid var(--border);
}
.panel-title { font-size: var(--font-lg); font-weight: bold; }
.file-count-badge {
  background: var(--accent);
  color: var(--text-on-accent);
  border-radius: var(--radius-pill);
  padding: 1px 6px;
  font-size: var(--font-xs);
}
.panel-content { flex: 1; overflow-y: auto; }

Dialog / Modal

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.dialog {
  width: 480px;
  max-width: 90vw;
  max-height: 80vh;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-popup);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.dialog-header {
  padding: 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.dialog-header h2 { font-size: var(--font-xl); }
.dialog-content { padding: 16px; overflow-y: auto; flex: 1; }
.dialog-actions {
  padding: 12px 16px;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  border-top: 1px solid var(--border);
}

Primary button: background: var(--accent); color: var(--text-on-accent); padding: 8px 16px; border-radius: var(--radius-lg); Secondary button: background: var(--bg-tertiary); color: var(--fg-secondary); same padding/radius. Danger button: background: var(--error); color: var(--text-on-error);

Form Controls

input, select, textarea {
  height: 36px;                        /* standard height */
  background: var(--bg-tertiary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--fg-primary);
  font-size: var(--font-md);
  padding: 0 8px;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  outline: none;
}

Toggle switch: 36×20px track, --radius-full (10px), off=--bg-tertiary, on=--accent. White thumb slides with 0.2s transition.

Range slider: 4px track height, 16px circular thumb in --accent.

Settings Panel

Full-screen overlay. Inner panel: --bg-secondary, 700px max-width, 80vh max-height.

  • Left sidebar with tabs (General, Notifications, Dictation, Terminal, Agents)
  • Right content area with sections
  • Section heading: <h3>, --font-lg, bold, margin-bottom: 12px
  • Settings row: flex space-between, label left, control right, padding: 8px 0

Status Badges Reference

PR State (sidebar .branch-pr-badge)

StateBackgroundBorderText ColorExtra
Open--successnone--text-on-success—
Drafttransparent1px dashed --fg-muted--fg-muted—
Merged#a371f7none--text-on-accent—
Closed--errornone--text-on-error—
Conflict--errornone--text-on-errorpulse-opacity 1.5s
CI Failed--errornone--text-on-errorbold
CI Pendingtransparent1px solid #e3b341#e3b341pulse-opacity 2s
Changes Req.#d29922none--text-on-accent—
Review Req.transparent1px solid #d29922#d29922—

All badges: font-size: --font-xs, font-family: --font-mono, font-weight: 600, border-radius: --radius-pill, padding: 1px 6px.

CI State (status bar)

StateBackgroundText
Successrgba(158,206,106,0.2)#9ece6a
Failurergba(247,118,142,0.2)#f7768e
Pendingrgba(224,175,104,0.2)#e0af68

Agent/Usage (tab + status bar)

StateCSS ClassStyle
Agent runningTab colored agent prefixTab has colored agent prefix badge
Usage normal.agentUsage--fg-secondary text
Usage ≥70%.agentUsageWarning#dcdcaa text (warning yellow)
Usage ≥90%.agentUsageCritical#f48771 text + pulse-opacity 2s
Rate limited.agentRateLimited#f44747 text + pulse-opacity 2s
Ticker warning (≥80 priority).tickerWarning#ffd700 text, gold bg at 0.1 alpha
Update available.updateBadge#4ec9b0 text, teal bg at 0.15 alpha

Icons

No icon library dependency. Icons are monochrome inline SVGs with fill="currentColor", so they inherit the surrounding text colour and recolour with the theme for free. Never emoji — AGENTS.md → “Visual”.

Text symbols and Unicode are still correct where the glyph is the content rather than an icon: the branch and state markers in the table below, and single characters inside a dense row. Reach for an inline SVG for anything that reads as an icon.

SymbolMeaningWhere
★Main/primary branchSidebar branch icon
YFeature branchSidebar branch icon
?Awaiting inputBranch icon (warning/orange, pulsing)
+Add/createButtons
×Close/removeTab close, panel close, dialog close
⋯Context menuRepo header
✎Edit/renameBranch double-click
▶Send/executeNotes panel send button
ChevronIconChevron (expand/collapse), SVG not textSidebar groups, repos, GitHub sections, search bar
●Tab status dotTab bar (grey=running, green=idle, purple=unseen, blue-pulse=activity, orange-pulse=awaiting, red-pulse=error)
⎇Git branch symbolStatus bar
💡Ideas panelStatus bar, panel header

Icon dimensions: 18px wide container for branch icons. --font-md or --font-lg size. Always left of text with gap: 6–8px.

Scrollbars

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--bg-highlight);
  border-radius: var(--radius-md);
}
::-webkit-scrollbar-thumb:hover { background: var(--fg-muted); }

Terminal scrollbar overridden to 8px with !important.

Interactive States

Hover

  • Background: one level up (--bg-secondary → --bg-tertiary, or --bg-tertiary → --bg-highlight)
  • Text: --fg-secondary → --fg-primary
  • Border: transparent → --accent (for add-repo button)
  • Duration: 0.1s

Active / Selected

  • Active branch: --bg-highlight bg + 2px solid var(--accent) left border
  • Active tab: --bg-secondary bg + 2px solid var(--accent) top border + --fg-primary text
  • Active toggle: --accent bg + white text
  • No hover animation on already-active items

Focus

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

Excluded on toggle buttons and mic button.

Disabled

  • opacity: 0.3 (strong) or 0.5 (mild)
  • cursor: not-allowed
  • No hover, no transitions

Hidden-until-hover

Pattern used for actions that clutter the UI when always visible:

  • Repo actions: opacity: 0; pointer-events: none; → opacity: 1; pointer-events: auto; on .repo-header:hover
  • Branch actions: max-width: 0; overflow: hidden; → max-width: 44px; on .branch-item:hover
  • Tab close button: opacity: 0 → opacity: 1 on .tab:hover

Platform Differences

PropertymacOSWindows/Linux
Toolbar height38px32px
Traffic light offset.platform-macos .toolbar-left { padding-left: 78px; }None
System font-apple-system firstSegoe UI (Win) / Roboto (Linux) first
Quit menuApp menuFile menu
Check for UpdatesApp menuHelp menu

CSS classes on <html>: .platform-macos, .platform-windows, .platform-linux.

Accessibility

  • Primary text (#cccccc on #1e1e1e): 10:1+ contrast ratio (exceeds WCAG AAA).
  • Secondary text (#a0a0a0 on #252526): 6:1+ (exceeds WCAG AA).
  • Status communicated by color + shape + icon — never color alone.
  • :focus-visible outlines for keyboard navigation.
  • prefers-reduced-motion query disables all animations.
  • Custom scrollbars maintain 8px touch target.

PWA / Mobile (src/mobile/mobile.css)

The mobile PWA has its own standalone stylesheet at src/mobile/mobile.css, completely independent from the desktop global.css. This allows the mobile UI to evolve separately while sharing the same design language.

What’s shared

  • Core color palette (same --bg-*, --fg-*, --accent, --success, --warning, --attention, --error variables with identical default values)
  • Border radius scale (--radius-sm through --radius-full, excluding --radius-xs)
  • Shadow tokens (--shadow-popup, --shadow-dropdown)
  • ANSI terminal palette

What differs

PropertyDesktop (global.css)Mobile (mobile.css)
Font mono stackJetBrains Mono, Fira Code, Hack, Cascadia, …SF Mono, Menlo, Consolas, DejaVu, …
Font size scale--font-3xs through --font-3xl (8–24px)Not defined — components use explicit px values
Layout variables--sidebar-width, --toolbar-height, --tab-bar-height, --status-heightNot used — mobile uses flex-based layout
User selectDisabled (none)Enabled (text)
Theme variables--activity, --merged, --unseenNot present (mobile has no terminal activity tracking yet)
Safe areasNot usedenv(safe-area-inset-top/bottom) on #mobile-app
Input font-sizeFrom scaleFixed 16px minimum to prevent iOS auto-zoom

Keeping in sync

When updating core palette colors in global.css, also update mobile.css — the :root blocks must stay aligned for the shared variables. Theme-specific variables (--activity, --merged, --unseen) are desktop-only and do not need mobile equivalents until that functionality ships in PWA.

Anti-Patterns (DO NOT)

  • No bright whites — max text brightness is --fg-primary (#cccccc).
  • No new shadows — only the three defined levels exist.
  • No transition: all — always list specific properties.
  • No hardcoded core colors — use CSS variables for the four bg levels, three fg levels, and status colors.
  • No icon libraries, no emoji — monochrome inline SVG with fill="currentColor".
  • No off-scale radius — only --radius-xs through --radius-full.
  • No !important — except terminal scrollbar overrides.
  • No pixel values outside the spacing scale unless component-specific dimension (like 28px repo initials).
  • No inline styles for theming — all colors and spacing via CSS variables (desktop: global.css, mobile: mobile.css).

Touch Sidebar Action Trays

Keep branch actions hidden at rest on touch devices. A left swipe reveals a full-height tray with neutral More, accent Add, and error-coloured Remove buttons, with at least 44px width per action. Only removable linked worktrees show Remove. Keep native vertical scrolling with touch-action: pan-y. Use the existing palette and radius tokens, and disable slide transitions when reduced motion is requested. Mouse hover controls retain their existing layout.

Rich working agent rows reserve two clamped intent lines. Idle, awaiting-input and error rows stay on one line with status dots; their full intent, task or prompt remains in the tooltip. Compact rows keep their one-line layout.

Rich navigation keeps compact typography and padding. Branch identity and facts share one line, with names truncated and full PR/lifecycle facts in tooltips. Repository metadata disappears when PR and worktree counts are both zero. Header controls use compact sizing to protect repo names. Returned subagents fold into an expandable count independently of running work.

The mobile update banner sits below the app header to keep its small text away from the translucent iOS status edge. It uses --mobile-update-foreground: black on Commander’s blue accent and white on Paper’s darker blue. The pressed state changes the background to --accent-hover instead of fading the whole strip. Do not reuse Commander’s shared white accent foreground here: at 13px, its 3.36:1 contrast does not meet WCAG AA.