hyperhive/docs/web-ui/css-vars.md
iris e9d59f3d10 docs: fix genuine passive-voice hits in docs/web-ui (small files)
agent.md, css-vars.md, design-guide.md, shape.md, terminal-rendering.md --
26 of 40 write-good.Passive hits rewritten to name the actor (hive-agent,
browsers, the harness, the client, lifecycle::{spawn,rebuild}, CSS, the
router, ...), reusing an actor already named nearby wherever one exists.
14 left alone: established config-state conditionals ("when X is
set/enabled/given/called"), negative-capability/state-descriptor idioms
("is gone", "is hidden", "is unchanged"), one false-positive
("typed slash commands" as a compound noun, not a passive action), a
backward-looking changelog fact with no actor worth naming, and two
deliberately-parallel rhetorical contrasts (sanitized vs XSS-safe;
cache-hit vs cache-miss) left symmetric on purpose.

dashboard.md (70 more hits) is the next docs/web-ui/ batch, not part of
this one -- big enough to deserve its own PR.
2026-09-08 15:54:07 +02:00

176 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 (for example one fed a stylix
base16 scheme, which is natively base00base0F) 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.)
`colors.css` declares each palette's 16 hex values exactly once —
`--mocha-baseNN` (dark, Catppuccin Mocha) and `--latte-baseNN` (light,
Catppuccin-Latte-derived; `base08`-`base0F` run darker than stock
Latte to actually clear WCAG AA against how this app uses them — real
contrast failures found in review, see the comment above those
declarations in `colors.css` for the numbers and why). Everything else
in the file just points the active `--baseNN` slot at one of those two
raw palettes via `var()`, never a second copy of a hex value: an
unconditional default (Mocha), a row gated behind `@media
(prefers-color-scheme: light)` (Latte, the OS/browser-level default),
and two `:root[data-theme='light'|'dark']` blocks a per-user override
sets (swarm-ui's `SettingsMenu`) — an attribute selector always
outranks the plain `:root` inside the media query, so the override wins
regardless of the actual OS preference. Absent an override, the media
query decides; that's what a client with no more specific theme
decision gets (see "Theme swapping" below for how a stylix deployment
supersedes all of this at once).
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 each page still
links `colors.css` 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** (for example 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 (for example 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.
**Two palettes, one contract.** `colors.css` ships both a dark default
(Catppuccin Mocha, unconditional) and a light default (Catppuccin Latte,
behind `@media (prefers-color-scheme: light)`), plus a per-user override
path (`:root[data-theme]`) — a theme generator that overrides the file
wholesale (for example the stylix path, which writes a single unconditional
`:root` block with no media query, no `--mocha-*`/`--latte-*` vars, no
`[data-theme]` blocks) supersedes all of it at once, same as today.
`colors.css` base16 slot defaults (Catppuccin Mocha, dark default):
| 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).