# Theme — token system, palette, typography ## Token identity rule Every visual surface gets its own named token. Never borrow a token from another surface just because they happen to resolve to the same color. **Wrong:** `sidebarBackground` for the hat bar (the hat isn't a sidebar). **Right:** Create `chromeBackground` that resolves to the same palette key. When two surfaces share a color: 1. **Same conceptual surface** (sidebar + context panel are both "side panels") → one shared token set is fine. 2. **Different surfaces that happen to match** (hat bar + sidebar + status bar are all "chrome frame") → create a shared primitive in the palette/semantic layer (e.g. `bgChrome`) and give each surface its own token that maps to that primitive. This lets themes diverge them later without breaking widgets. ## Palette depth primitives The palette layer has these depth primitives (defined in each theme YAML): - `bg` (`#20202C`) — outermost root, behind everything - `bgSunken` (`#1A1A24`) — chrome frame: sidebar, hat, statusbar - `surface` (`#242838`) — elevated: pane headers, active tabs - `surfaceHi` (`#2C3046`) — interactive: hover states, selections Chrome tokens (`chromeBackground` / `chromeForeground` / `chromeBorder`) are the shared root for all frame surfaces. They resolve to `bgSunken` / `textDim` / `border` in the palette. Themes can override them to diverge hat from sidebar from status bar if desired. ## Typography Three constants — never hardcode sizes or families: ``` family UI → inherited from DefaultTextStyle (JosefinSans Light 300) family mono → clideMonoFamily (JetBrainsMono) body size → clideFontBody (15) caption size → clideFontCaption (14) — status bar, section headers, git info mono size → clideFontMono (14) — terminal, code, paths, IDs ``` Use `ClideText` for themed text. Set `muted: true` for secondary text (resolves to `globalTextMuted`). Set `fontFamily: clideMonoFamily` for code/paths/IDs. Don't set fontFamily for UI text — it inherits. ## Extension-owned domain colors Extensions that need domain-specific color coding (ticket types, decision types, priority levels) should NOT add tokens to `SurfaceTokens`. Instead: 1. Create a color map class in the extension (e.g. `TicketTypeColors`). 2. Ship dark and light presets, auto-selected via `ClideTheme.of(context).dark`. 3. Store user overrides under `ext..colors` in settings. 4. Reference: `lib/builtin/tickets/src/ticket_colors.dart`. This keeps the core token surface lean and lets each extension own its palette. The pattern scales to any extension needing domain colors. ## Named themes are user contracts — never retune their palette (D-69) A bundled theme that ships under a recognisable name (`clide`, `midnight`, `paper`, `terminal`, `catppuccin-*`, …) is a **user contract**. Its palette — including syntax tokens, status colours, and `borderHi` — **MUST NOT be retuned to pass a contrast gate**. Users pick Catppuccin because it looks like Catppuccin; quietly darkening a swatch to clear WCAG changes what they signed up for. Don't "fix" the artist's vision. When a theme fails the contrast gate ([D-22](../../../governance/decisions/accessibility.md)), the fix is **a sibling `-hc` (high-contrast) theme**, not an edit to the faithful one (D-69): - Keep `foo.yaml` pixel-faithful to the source palette. - Add `foo-hc.yaml` — same silhouette, but muted text / status chips / syntax tokens / focus border bumped to clear the strict (`extendedPairs`) gate. Only `-hc`/`-cb` variants are held to the extended set; base themes pass the baseline (`canonicalPairs`) only. - Register both in `main.dart` `_loadBundledThemes` **and** the bundled list in `test/a11y/contrast_test.dart`. Mechanics: `canonicalPairs` (baseline, every theme) vs `extendedPairs` (strict, `-hc`/`-cb` only) live in `lib/kernel/src/theme/contrast.dart`. If a faithful palette is so soft it can't clear even the *baseline* chrome pairs, that's a design call — surface it, don't silently retune. (Catppuccin Latte hit exactly this on two chrome pairs.) This is the same split VS Code ships: `Default Dark+` plus a separate `Default High Contrast`. ## Where to look in the codebase - Full token list: `lib/kernel/src/theme/tokens.dart` - Resolver fallbacks: `lib/kernel/src/theme/resolver.dart` - Theme YAML example: `lib/kernel/src/theme/themes/clide.yaml` - Decision: D-43 (handoff), D-44 (four bundled themes), D-45 (syntax tokens)