# CSS custom properties (theme variables) Colour variables live in **two standalone stylesheets**, split so a theme swap touches only the first: 1. **`colors.css`** — the 16 `--base00`…`--base0F` base16 slots. **This is the entire theme swap contract.** A generator (e.g. one fed a stylix base16 scheme, which is natively base00–base0F) replaces _only this file_. 2. **`theme.css`** — the semantic layer: `--bg`, `--fg`, `--purple`, … _derived_ from the base16 slots (`--bg: var(--base00)` etc.). This is what the app references and it **never changes on a swap**. Both files live in `frontend/packages/shared/src/`. Per-page stylesheets (`common.css`, `dashboard.css`, `flow.css`, `logs.css`, `agent.css`) reference the **semantic** names and must **not** redeclare them or reach for the raw `--baseNN` slots directly. (`base.css` holds only the shared `body` typography — it references the palette but no longer defines it.) Both are deliberately standalone, not `@import`ed into the page bundles: each package re-exports them (`src/{colors,theme}.css` → `@import "@hive/shared/…"`) so esbuild emits its own `dist/static/colors.css` + `dist/static/theme.css`, and every page links them **first** (`colors.css` then `theme.css`) ahead of the page CSS. Note `theme.css` does **not** `@import` `colors.css` — that would bake the slots back into it; they're two separate output files so a swap replaces just `colors.css`. (Load order doesn't actually affect resolution — custom properties resolve at computed-value time — but `colors.css` is linked first for clarity.) ## Semantic palette (`theme.css`) Each semantic var derives from a base16 slot (or, for the three that have no clean slot, a `color-mix()` over base16 — pixel-identical under the default palette). Default hexes shown are Catppuccin Mocha. | Variable | Derives from | Default hex | Use | | -------------- | --------------------------- | ----------- | --------------------------------------------------------------- | | `--bg` | `base00` | `#1e1e2e` | page background | | `--bg-elev` | `base01` | `#181825` | elevated surfaces: floating dropdowns, popovers | | `--border` | `base02` | `#313244` | general borders, hover/active backgrounds | | `--purple-dim` | `base03` | `#45475a` | subtle borders, terminal chrome, badge backgrounds | | `--fg` | `base05` | `#cdd6f4` | primary text colour | | `--red` | `base08` | `#f38ba8` | errors, fail state | | `--amber` | `base09` | `#fab387` | warnings, pending / running state | | `--yellow` | `base0A` | `#f9e2af` | flash messages, mild warnings | | `--green` | `base0B` | `#a6e3a1` | success, ok state | | `--cyan` | `base0C` | `#89dceb` | tool-use events, info accents | | `--blue` | `base0D` | `#89b4fa` | links, interactive accent (distinct from cyan) | | `--purple` | `base0E` | `#cba6f7` | accent — active tabs, links, highlights | | `--pink` | `base0F` | `#f5c2e7` | thinking events | | `--crust` | `mix(base00 58%, #000)` | `#11111b` | terminal / code block background (below `--bg`) | | `--muted` | `mix(base05 55.5%, base00)` | `#7f849c` | secondary / dimmed text | | `--subtext0` | `mix(base05 77.7%, base00)` | `#a6adc8` | toolbar/status text; dimmer than `--fg`, lighter than `--muted` | ## Common mistakes The Catppuccin colour names do **not** map 1:1 to the variable names. Variables to avoid (undefined — they will silently resolve to transparent / inherited): | Wrong | Correct | | -------------------------- | --------------------------------------------------- | | `--text` | `--fg` | | `--mauve` | `--purple` | | `--surface0` | `--bg-elev` (float bg) or `--border` (border/hover) | | `--surface1` | `--border` | | `--surface2` | `--purple-dim` | | `--overlay0`, `--overlay1` | `--muted` | | `--base`, `--mantle` | `--bg`, `--bg-elev` | ## Usage guide **Floating menus and dropdowns** (e.g. agent context menu, tabbar overflow): ```css background: var(--bg-elev); border: 1px solid var(--purple-dim); ``` **Hover / active state backgrounds**: ```css background: var(--border); ``` **Active tab text / accent elements**: ```css color: var(--purple); ``` **Muted / meta text**: ```css color: var(--muted); ``` **Error / warning / success badges**: ```css color: var(--red); /* error */ color: var(--amber); /* warning / running */ color: var(--green); /* ok */ ``` ## Theme swapping — the base16 contract **The swap interface is `colors.css` — the 16 base16 slots, not our semantic names.** A theme generator (e.g. one reading a stylix base16 scheme) overrides only `colors.css`; the semantic layer in `theme.css` derives everything else, so the whole UI re-themes with nothing else to template or regenerate. The base16 slot → semantic mapping is _internal_ to `theme.css` (the "Derives from" column above) — a generator never needs to know our var names, and `theme.css` + the page bundles stay untouched. `colors.css` base16 slot defaults (Catppuccin Mocha): | Slot | Default | Standard base16 role | Mapped to | | -------- | --------- | -------------------- | --------------------------------------------------- | | `base00` | `#1e1e2e` | default bg | `--bg`, (darkened) `--crust` | | `base01` | `#181825` | lighter bg | `--bg-elev` | | `base02` | `#313244` | selection/surface | `--border` | | `base03` | `#45475a` | comments/dim surface | `--purple-dim` | | `base04` | `#585b70` | dark foreground | _(unused; kept for completeness)_ | | `base05` | `#cdd6f4` | default foreground | `--fg`, (blended) `--muted`/`--subtext0` | | `base06` | `#f5e0dc` | light foreground | _(unused)_ | | `base07` | `#b4befe` | lightest | _(unused)_ | | `base08` | `#f38ba8` | red | `--red` | | `base09` | `#fab387` | orange | `--amber` | | `base0A` | `#f9e2af` | yellow | `--yellow` | | `base0B` | `#a6e3a1` | green | `--green` | | `base0C` | `#89dceb` | cyan | `--cyan` (our _sky_; Catppuccin's `base0C` is teal) | | `base0D` | `#89b4fa` | blue | `--blue` | | `base0E` | `#cba6f7` | magenta | `--purple` | | `base0F` | `#f5c2e7` | extra accent | `--pink` | Notes for theme authors: - **`--crust`, `--muted`, `--subtext0` have no own slot** — they're derived via `color-mix()` over base16 (`--crust` = a darkened `base00`; `--muted`/`--subtext0` = `base05`↔`base00` blends). They still track a swap automatically; no generator action needed. - A standard Catppuccin base16 scheme uses **teal** for `base0C`; we default it to **sky** (`#89dceb`) to preserve the historical accent. A stylix Catppuccin scheme will shift `--cyan` to teal — that's the operator's chosen scheme, working as intended. - `base04`/`base06`/`base07` aren't consumed by a semantic var today; they're kept at their standard values so the 16-slot contract is complete (a partial scheme override still works).