The flow page showed the raw broker message stream and had a compose box. This removes the page and its bundle, its link in the pages menu, and the styles only it used. The history endpoint that backfilled it and the operator-send endpoint behind the compose box had no other caller, so they go too, along with the broker query behind the history endpoint. The stream endpoint stays: the dashboard tabs still subscribe to it. The docs lose the FL0W section and the references to the two endpoints.
10 KiB
CSS custom properties (theme variables)
Colour variables live in two standalone stylesheets, split so a theme swap touches only the first:
colors.css— the 16--base00…--base0Fbase16 slots. This is the entire theme swap contract. A generator (for example one fed a stylix base16 scheme, which is natively base00–base0F) replaces only this file.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, 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 @imported 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%, #000000) |
#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 don't 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):
background: var(--bg-elev);
border: 1px solid var(--purple-dim);
Hover / active state backgrounds:
background: var(--border);
Active tab text / accent elements:
color: var(--purple);
Muted / meta text:
color: var(--muted);
Error / warning / success badges:
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 the
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 the semantic 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 (named sky here; Catppuccin's base0C is teal) |
base0D |
#89b4fa |
blue | --blue |
base0E |
#cba6f7 |
magenta | --purple |
base0F |
#f5c2e7 |
extra accent | --pink |
Notes for theme authors:
--crust,--muted,--subtext0have no own slot — they're derived viacolor-mix()over base16 (--crust= a darkenedbase00;--muted/--subtext0=base05↔base00blends). They still track a swap automatically; no generator action needed.- A standard Catppuccin base16 scheme uses teal for
base0C;colors.cssdefaults it to sky (#89dceb) to preserve the historical accent. A stylix Catppuccin scheme will shift--cyanto teal — that's the operator's chosen scheme, working as intended. base04/base06/base07aren'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).