refactor(frontend): base16 theme contract in a swappable colors.css
Split the palette into two standalone stylesheets: - colors.css — the 16 base16 slots (--base00..--base0F). This is the entire theme swap contract; a generator (e.g. a stylix base16 scheme, which is natively base00-base0F) replaces only this file. - theme.css — the semantic layer (--bg, --purple, …) derived from the base16 slots via var()/color-mix. Never changes on a swap. Every page links colors.css then theme.css; theme.css does NOT @import colors.css (that would re-bake the slots into it) — they're separate dist outputs so a swap touches just colors.css. Pixel-identical refactor: base16 defaults are Catppuccin Mocha and the three off-slot vars (--crust, --muted, --subtext0) derive via color-mix reproducing their exact prior hexes. Wires colors.css through both build.mjs CSS entry lists, the @hive/shared exports map, and all 7 page templates. css-vars.md + the frontend.nix output-list comment updated.
This commit is contained in:
parent
742d4a5c6d
commit
09787dd1c3
18 changed files with 204 additions and 84 deletions
|
|
@ -1,41 +1,57 @@
|
|||
# CSS custom properties (theme variables)
|
||||
|
||||
All colour variables are declared **once** in
|
||||
`frontend/packages/shared/src/theme.css` under `:root`.
|
||||
Per-page stylesheets (`common.css`, `dashboard.css`, `flow.css`, `logs.css`, `agent.css`) must reference these
|
||||
names and must **not** redeclare them. (`base.css` holds only the
|
||||
shared `body` typography — it references the palette but no longer
|
||||
defines it.)
|
||||
Colour variables live in **two standalone stylesheets**, split so a theme
|
||||
swap touches only the first:
|
||||
|
||||
`theme.css` is deliberately a **standalone** stylesheet, not `@import`ed
|
||||
into the page bundles: each package re-exports it (`src/theme.css` →
|
||||
`@import "@hive/shared/theme.css"`) so esbuild emits its own
|
||||
`dist/static/theme.css`, and every page links it **first**
|
||||
(`<link rel="stylesheet" href=".../theme.css">`) ahead of the page CSS.
|
||||
Because the palette lives in its own output file, a theme swap — e.g. a
|
||||
stylix-generated variant carrying the same variable names — can replace
|
||||
just that one file without rebuilding the rest of the frontend bundle.
|
||||
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**.
|
||||
|
||||
## Palette (`theme.css`)
|
||||
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.)
|
||||
|
||||
| Variable | Hex | Catppuccin Mocha | Use |
|
||||
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` | `#1e1e2e` | base | page background |
|
||||
| `--bg-elev` | `#181825` | mantle | elevated surfaces: floating dropdowns, popovers |
|
||||
| `--crust` | `#11111b` | crust | terminal / code block background |
|
||||
| `--fg` | `#cdd6f4` | text | primary text colour |
|
||||
| `--muted` | `#7f849c` | overlay1 | secondary / dimmed text |
|
||||
| `--purple` | `#cba6f7` | mauve | accent — active tabs, links, highlights |
|
||||
| `--purple-dim` | `#45475a` | surface1 | subtle borders, terminal chrome, badge backgrounds |
|
||||
| `--cyan` | `#89dceb` | sky | tool-use events, info accents |
|
||||
| `--blue` | `#89b4fa` | blue | links, interactive accent (distinct from sky) |
|
||||
| `--pink` | `#f5c2e7` | pink | thinking events |
|
||||
| `--amber` | `#fab387` | peach | warnings, pending / running state |
|
||||
| `--yellow` | `#f9e2af` | yellow | flash messages, mild warnings |
|
||||
| `--green` | `#a6e3a1` | green | success, ok state |
|
||||
| `--red` | `#f38ba8` | red | errors, fail state |
|
||||
| `--border` | `#313244` | surface0 | general borders, hover/active backgrounds |
|
||||
| `--subtext0` | `#a6adc8` | subtext0 | secondary toolbar/status text; dimmer than `--fg`, lighter than `--muted` |
|
||||
| `--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
|
||||
|
||||
|
|
@ -81,3 +97,49 @@ 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).
|
||||
|
|
|
|||
Loading…
Reference in a new issue