Files
soleprint/docs/data/en/themes.md
2026-08-11 07:30:27 -03:00

4.3 KiB
Raw Blame History

Themes

Three themes ship, and the same palettes drive the diagrams as well as the pages. soleprint is the default; the others are switched to on purpose.

Theme Reads as For
soleprint dark, rounded, amber the default — tools and dev chrome
mcrn dark, square, monospace, orange glow the terminal look, matched to mariano.mcrn.ar
lucid light, hairline, printable regulated documents, and sitting beside a real lucid.app export

Switch with ?theme=lucid, or the toggle in the corner. The choice persists.

Where it lives

soleprint/common/theme/
  tokens.css          the vocabulary + neutral defaults
  themes/*.css        one file per theme
  theme.js            resolve, apply, remember
  bake.py             inline the defaults into pages

Served together at /theme.css — tokens first, then every theme, each scoped to [data-theme="..."]. Adding a theme is adding a file: run.py lists the directory rather than carrying a hardcoded list.

Two naming families, one set of values

Both are answered, because both were already in use and renaming across a dozen templates would have been the larger change:

  • --bg / --surface / --border / --text / --muted / --accent — the station tools and the docs site
  • --surface-0..3 / --text-primary / --panel-radiuscommon/ui's Vue components

The second family is derived from the first in tokens.css, so a page using either name gets the same colour and a theme author fills in one set.

Order matters

<!-- baked defaults -->     <style>:root {  }</style>
<link rel="stylesheet" href="/theme.css">
<style> the page's own rules </style>

The theme has to load before the page's styles, so its element defaults underpin the page rather than override it. Get this backwards and tokens.css's body { background: var(--bg) } flattens whatever the page wanted — which is exactly how artery, atlas and station briefly lost their coloured content columns.

Baked defaults, and why

/theme.css is an absolute path, and soleprint is not always at the root — in a room's nginx it sits under /spr/ while location / goes to the frontend. A page that says background: var(--bg) and never receives --bg does not fall back to something plainer: the declaration is invalid at computed-value time, so the background goes transparent and the text goes initial-black on a design that assumed dark. Unstyled, not merely unbranded.

So every page carries a generated :root block before the link. Both are :root, so document order decides: the served stylesheet wins when it loads, and the baked block is what is left when it does not.

python3 common/theme/bake.py           # regenerate
python3 common/theme/bake.py --check   # fail if a page is stale

Only the variables a page actually uses are emitted, so the blocks stay small.

No webfonts

The stacks name faces that exist on the target rather than fetching any:

--font-ui:   "Segoe UI", Inter, system-ui, -apple-system, Arial, sans-serif;
--font-mono: "Cascadia Mono", "JetBrains Mono", Consolas, "SF Mono", monospace;

Segoe UI and Consolas ship with Windows. A regulated network blocks fonts.googleapis.com and file:// stalls on it, and neither failure looks like a missing font — they look like a broken page.

Diagrams follow

docs/graphs/themes/*.gvpr carry the same palettes for graphviz, so a diagram and the page around it are one visual language. See the graphs README and Export / Compile.

make docs graphs          # every graph, every theme

Diagrams are baked per theme rather than styled by CSS, because the docs embed them with <img src=…> — which makes the SVG a separate document the page's stylesheet cannot reach. A plexus that inlines the SVG can style it live, and the bundle plexus does exactly that.

Contrast

lucid is the first light theme, and the palette was measured rather than guessed — against both #ffffff and the #f5f7fa panel, at the sizes actually used. --dim drives 11px notes and --status-warn drives 10px labels, so both need 4.5:1 rather than the 3:1 large text gets away with. The obvious lighter greys came in at 3.43.8 and were dropped for that reason.