Adom Theme
Public Made by Adomby adom
The canonical Adom application theme spec: five schemes, exact tokens, font pairings, and the implementation contract.
name: adom-theme-system description: THE Adom application theme system, the canonical spec every Adom desktop app implements (Hydrogen Desktop is the reference; Adom Desktop is the next implementer). Five colour schemes with exact tokens, slugs vs product labels, per-scheme font pairings, the VS Code theme contract, storage migration rules, and the guard scripts that keep implementations honest. Trigger words - adom theme, adom themes, color scheme, colour scheme, theme tokens, adom design tokens, implement adom themes, adom studio theme, kickstand, adom slate, bright inverse, scheme slugs, adom fonts, satoshi, jetbrains mono, familjen grotesk, vs code adom theme.
The Adom Theme System
One theme system for every Adom desktop surface. Hydrogen Desktop (HD) is the REFERENCE IMPLEMENTATION; this skill is the contract for implementing the same system elsewhere, starting with Adom Desktop (AD). The design was measured into place on 2026-07-25 (contrast maths, live pixel sampling, in-workbench font probes), so treat the numbers here as decisions, not suggestions.
Source of truth files (HD repo, hydrogen-desktop - PRIVATE):
src/routes/styles.css- the token set and all five scheme blockstools/gen-vscode-themes.mjs- generates the VS Code theme pack FROM the tokens (pack v7.2.0)scripts/check-scheme-parity.sh+scripts/check-installed-themes.sh- the guardsscripts/install-brand-fonts.sh- webfont delivery into code-serversrc/lib/stores/settings.ts- FONTS_FOR_SCHEME, slugs, labels, migrations
Because that repo is private, the wiki page carries usable copies (pinned @ a4c764be, pack v7.2.0) at https://wiki.adom.inc/adom/adom-theme-system :
tokens.css- all five scheme blocks as drop-in CSS custom propertiesvscode/- the complete generated theme pack; copy into~/.local/share/code-server/extensions/adom.adom-themes-<version>/(the page's install.sh does this automatically when code-server is present)fonts/- the REDISTRIBUTABLE faces only: JetBrains Mono + Familjen Grotesk (both OFL 1.1, license texts beside the files). Satoshi binaries are NOT there and must never be uploaded to any public server (Fontshare EULA);fonts/satoshi/README.mddocuments the official Fontshare endpoints and the page's install.sh fetches the faces from them automatically. Full rules:fonts/LICENSES.mdon the page.reference/- byte-exact copies of the generator, template, font installer, and guards
1. The five schemes
| slug (storage key) | product label | character |
|---|---|---|
studio |
Adom Studio | THE DEFAULT (John 2026-07-27; this design briefly staged as 'Adom Studio v2', then took over the name — zero-migration rename). VS Code's Dark 2026 architecture re-grounded on the brand: body #161b22, base text #c9d1d9, every gray on the brand slate hue at 2026's exact contrast steps, product blue re-issued as Adom teal, syntax untouched. Generated by reference/gen-adom-studio-v2.mjs from reference/reference-2026-dark.json. The old template-projected Studio is abandoned. |
studio-bright |
Adom Studio Dark (Brighter Text) | Everything on the deep ground, brighter interface text. |
studio-dark |
Adom Studio Dark | The original screen-tuned dark set, quieter text. This is the TOKEN BASE. |
kickstand |
Adom Kickstand | The design firm's identity palette: #191919 ground, muted navy chrome, headline face in prose. |
slate |
Adom Slate | Calm cool blue-grey; uses the code font Windows already ships. |
Invariant history: the old "Adom Studio == Adom Studio Dark (Brighter Text) with --bg/--surface
traded" parity guard applied to the ABANDONED template-projected Studio and is retired with
it. The new invariant for Adom Studio (since 2026-07-27) is: it is a pure function of
reference/reference-2026-dark.json + the two brand anchors, regenerated by
reference/gen-adom-studio-v2.mjs — never hand-edited. check-scheme-parity.sh still
guards the APP schemes (studio vs studio-bright tokens in styles.css); it no longer
constrains the VS Code Studio theme.
2. The tokens, per scheme (generated from styles.css, do not hand-edit)
| token | Adom Studio | Adom Studio Dark (Brighter Text) | Adom Studio Dark | Adom Kickstand | Adom Slate |
|---|---|---|---|---|---|
--bg |
#161b22 |
#0d1117 |
#0d1117 |
#191919 |
#17212e |
--surface |
#0d1117 |
#161b22 |
#161b22 |
#202127 |
#202a37 |
--elevated |
#1c2128 |
#1c2128 |
#1c2128 |
#282a30 |
#28323f |
--overlay |
#21262d |
#21262d |
#21262d |
#0e1c31 |
#313b48 |
--border |
#30363d |
#30363d |
#30363d |
#3d3f46 |
#3d4754 |
--text |
#e6edf3 |
#e6edf3 |
#e6edf3 |
#f5f7f9 |
#f0f6fc |
--text-body |
#c9d1d9 |
#c9d1d9 |
#c9d1d9 |
#d7dde3 |
#d3e0ef |
--text-2 |
#c9d1d9 |
#c9d1d9 |
#8b949e |
#97a0aa |
#93a6bd |
--text-3 |
#6e7681 |
#6e7681 |
#484f58 |
#5a626b |
#4a5a70 |
--accent |
#00b8b1 |
#00b8b1 |
#00b8b1 |
#00b8b1 |
#00b8b1 |
--accent-hover |
#00d4cb |
#00d4cb |
#00d4cb |
#00d4cb |
#00d4cb |
--accent-bright |
#00e6dc |
#00e6dc |
#00e6dc |
#8fd3d4 |
#5fe6de |
--on-accent |
#05221f |
#05221f |
#05221f |
#003c3f |
#04211f |
--blue |
#64abff |
#64abff |
#64abff |
#64abff |
#64abff |
--blue-fill |
#0061ef |
#0061ef |
#0061ef |
#0061ef |
#0061ef |
--purple |
#8c6bf7 |
#8c6bf7 |
#8c6bf7 |
#8c6bf7 |
#8c6bf7 |
--green |
#3fb950 |
#3fb950 |
#3fb950 |
#3fb950 |
#3fb950 |
--red |
#f85149 |
#f85149 |
#f85149 |
#f85149 |
#f85149 |
--yellow |
#d29922 |
#d29922 |
#d29922 |
#d29922 |
#d29922 |
Rationale you must not undo:
- Body text lives in the 9-13:1 contrast band. Below strains; above ~14:1 dark-mode text haloes (that is why Adom Studio's primary text is 13.0:1, not 15.8:1).
- The teal accent must stay >= 6:1 on every ground. That constraint is why there is no teal or purple GROUND: they were built and measured into failure.
- Five distinct dark grounds are impossible inside those constraints (closest achievable pair measured 24 apart, i.e. invisible). The family therefore differentiates on AXES: hue (Slate), elevation (Adom Studio), legibility (Bright), accents (Kickstand).
--blue-fill(#0061ef) is 3.6:1 on dark grounds: FILLS AND ACCENTS ONLY, never text. Links and informational text use--blue(#64abff).--green/--red/--yelloware semantic (success/destructive/warning) and nothing else. A settings confirmation is not "success"; use the accent.- Kickstand's chrome navy is a DERIVATION, not a new colour: mix(#00204f 45%, #191919 55%) = #0e1c31. Full-strength navy as a dropdown fill read as "that weird blue" and was demoted to accents only.
- Kickstand's derived chrome greys are COOL-SHIFTED (correction, 2026-07-26): the firm's PDF
specifies only the #191919 Black ground, which stays byte-exact. The greys we derived from
it (
--surface#202127,--elevated#282a30,--border#3d3f46) carry a few points of blue because pure neutral grey beside the blue-tinted sibling schemes and the wiki is perceived as warm brown (simultaneous contrast; the shipped pixels measured exactly neutral, and still read "brownish"). Implementers copying Kickstand must copy these corrected values, not re-derive neutral ones from the ground.
3. Token roles (what drives what)
| token | role |
|---|---|
--bg |
the app's deepest ground / the editor sheet |
--surface |
panels, toolbars, dialogs |
--elevated |
hover surfaces, raised cards, and THE ACTIVE TAB (always the raised sheet; mapping tabs to --surface inverts into a black tab in Adom Studio) |
--overlay |
menus, dropdowns, tooltips, popovers |
--text |
emphasis only (headings, active items) |
--text-body |
running text |
--text-2 / --text-3 |
secondary / disabled-borders-only (text-3 fails AA as text) |
4. Slugs vs labels, and the migration law
Slugs are storage keys (persisted preferences, data attributes, CSS selectors). Labels are product names, and they are EXACTLY the VS Code theme names, so one look has one name on every surface.
History that bites: on 2026-07-25 the slug studio CHANGED MEANING (it was the dark base,
it is now the flagship). Any store that ever held old values must translate them ONCE under
old semantics, guarded by a version marker, because the string alone cannot say which era
wrote it. HD's pattern: a NEW localStorage key (hd-color-scheme2) whose legacy-key read
translates {studio->studio-dark, studio-bright-inverse->studio, inverse->studio,
blueprint->slate, contrast->studio-bright, graphite->slate} then deletes the legacy key;
plus desktop.scheme_slug_version = 2 on the saved-preference layer. If AD stores scheme
state, implement the same translation once, and NEVER reuse a slug again without bumping the
version and extending the map.
Unknown/retired slug at load time: fall back to studio explicitly. Note the base tokens
ARE studio-dark's look, so "no attribute set" silently paints Dark, not the default: always
set the scheme attribute explicitly.
5. Fonts (part of the theme, three faces only)
Brand faces: Familjen Grotesk (headline), Satoshi (body/prose), JetBrains Mono (code, SIL OFL 1.1 - ship the licence with the files). Do not add faces; variety for its own sake is how a design system becomes a font menu.
Per-scheme pairing (FONTS_FOR_SCHEME):
| scheme | code stack | prose stack |
|---|---|---|
| studio, studio-bright, studio-dark | 'JetBrains Mono', 'Cascadia Code', Consolas, ui-monospace, monospace |
Satoshi, -apple-system, 'Segoe UI', sans-serif |
| kickstand | same code stack | 'Familjen Grotesk', Satoshi, -apple-system, 'Segoe UI', sans-serif |
| slate | 'Cascadia Code', Consolas, 'JetBrains Mono', ui-monospace, monospace |
Satoshi stack |
Delivery, learned the hard way:
- Webfonts must be served from YOUR OWN origin with ABSOLUTE paths. HD's relative
./adom-fonts/...URLs 404'd for weeks while a Windows-installed copy silently covered for them; nothing anywhere admitted it. - Isolated webviews (the Claude chat panel) do NOT inherit your page's webfonts. Their brand faces resolve only from WINDOWS-INSTALLED fonts, so the installer must keep shipping Satoshi (and Familjen) as Windows fonts even when the main app uses webfonts.
- A fallback chain never fails, it silently falls through. Any font UI must display the RESOLVED family, measured in the document that renders it, not the configured first name.
- VS Code key trap:
chat.fontFamilyis the chat PROSE;chat.editor.fontFamilyis the chat's CODE BLOCKS. They read backwards and were once wired backwards.
6. The VS Code theme contract
The editor themes are GENERATED from the tokens (HD tools/gen-vscode-themes.mjs), one
theme per scheme, labels identical to the picker labels. Rules:
- An unknown
workbench.colorThemename silently falls back to VS Code's DEFAULT LIGHT theme. Renames must therefore update the installed pack AND the live setting in one step, and a machine-side guard (check-installed-themes.sh) should verify the installed pack, because repo-vs-repo checks cannot see a stale machine. - The generator wipes its output dir before writing so a renamed theme cannot leave a stale file behind.
- Current pack: adom.adom-theme (Adom Studio / Adom Studio Dark (Brighter Text) / Adom Studio Dark / Adom Kickstand / Adom Slate), version-locked to this package: the extension version and the package version are THE SAME NUMBER, bumped together in the same commit, every time.
- The extension is NEVER in any marketplace/gallery, so gallery update checks return null
for it by design; the only update channel is
adom-wiki pkg update. Currency check = installed adom.adom-theme version vs this package version. - Legacy: before 2026-08-02 this was
adom-theme-system+adom.adom-themes(7.x line); install.sh purges the old extension id on every install.
Delivering the pack: install it, never copy it
Copying the pack folder into ~/.local/share/code-server/extensions/ does not
install it. The folder is absent from that directory's extensions.json, and
code-server PRUNES unregistered folders on its next launch. The pack disappears
before the user ever sees it, the picker lists zero "Adom ..." themes, and a
workbench.colorTheme of "Adom Studio" then hits the unknown-name rule above and
falls back to stock Light. This shipped in a golden image and rendered the whole
editor off-brand; install.sh now packages a .vsix and installs it properly.
Three traps worth knowing before you write your own installer:
code-server --install-extensiontakes a.vsix, not a directory. Build one at install time: it is a zip ofextension/<the pack>plusextension.vsixmanifestand[Content_Types].xml. Novsce, no network, python'szipfileis enough.- The
code-serveronPATHinside an editor terminal is the remote-cli shim and cannot install anything. Resolve the real launcher (/usr/lib/code-server/bin/ code-serverand friends) and reject any path containingremote-cli. - The CLI dies with
error not spawned with IPCwhen it inheritsCODE_SERVER_PARENT_PID(andVSCODE_IPC_HOOK_CLI) from the editor terminal that is running the installer.env -uboth before invoking it.
The litmus is registration, not files on disk: assert the adom.adom-themes id
appears in extensions.json after installing. A directory check passes for a pack
that is about to be deleted, which is exactly how this got past a build gate.
Uninstall through the CLI too, so the registration goes with the folder.
7. Implementing in Adom Desktop, the checklist
- Define the tokens as CSS custom properties on
:root(the studio-dark values) plus one[data-scheme='<slug>']block per scheme copied from the table above. Adom Studio's block should contain ONLY Bright's text ramp plus the bg/surface swap. - Route every colour through the tokens. No hardcoded hex: HD carried 4,641 hardcoded values before the sweep and that is why its panels never matched each other.
- Picker UI: show the LABELS, store the SLUGS, default
studio, apply via the data attribute before first paint (read the cache directly, do not wait for stores). - Fonts follow the scheme's pairing; if you expose font pickers, overrides beat the pairing, "Match theme" means follow it, and the UI must name what that currently means.
- Copy the guards: scheme parity, brand-teal component matching (one colour has at least five CSS spellings; match rgb components, not strings), installed-theme verification if you install editor themes.
- Do not silently restyle approval/security surfaces with scheme colours that weaken semantics: amber stays the heavy-action colour, red stays destructive.
Version
Spec v1.0.0, extracted from hydrogen-desktop @ 2026-07-26 (pack v7.1.0). When HD's tokens change, regenerate section 2 from styles.css rather than editing it by hand.
---
name: adom-theme-system
description: THE Adom application theme system, the canonical spec every Adom desktop app implements (Hydrogen Desktop is the reference; Adom Desktop is the next implementer). Five colour schemes with exact tokens, slugs vs product labels, per-scheme font pairings, the VS Code theme contract, storage migration rules, and the guard scripts that keep implementations honest. Trigger words - adom theme, adom themes, color scheme, colour scheme, theme tokens, adom design tokens, implement adom themes, adom studio theme, kickstand, adom slate, bright inverse, scheme slugs, adom fonts, satoshi, jetbrains mono, familjen grotesk, vs code adom theme.
---
# The Adom Theme System
One theme system for every Adom desktop surface. Hydrogen Desktop (HD) is the REFERENCE
IMPLEMENTATION; this skill is the contract for implementing the same system elsewhere,
starting with Adom Desktop (AD). The design was measured into place on 2026-07-25 (contrast
maths, live pixel sampling, in-workbench font probes), so treat the numbers here as decisions,
not suggestions.
Source of truth files (HD repo, hydrogen-desktop - PRIVATE):
- `src/routes/styles.css` - the token set and all five scheme blocks
- `tools/gen-vscode-themes.mjs` - generates the VS Code theme pack FROM the tokens (pack v7.2.0)
- `scripts/check-scheme-parity.sh` + `scripts/check-installed-themes.sh` - the guards
- `scripts/install-brand-fonts.sh` - webfont delivery into code-server
- `src/lib/stores/settings.ts` - FONTS_FOR_SCHEME, slugs, labels, migrations
Because that repo is private, the wiki page carries usable copies (pinned @ a4c764be, pack
v7.2.0) at https://wiki.adom.inc/adom/adom-theme-system :
- `tokens.css` - all five scheme blocks as drop-in CSS custom properties
- `vscode/` - the complete generated theme pack; copy into
`~/.local/share/code-server/extensions/adom.adom-themes-<version>/` (the page's install.sh
does this automatically when code-server is present)
- `fonts/` - the REDISTRIBUTABLE faces only: JetBrains Mono + Familjen Grotesk (both OFL 1.1,
license texts beside the files). Satoshi binaries are NOT there and must never be uploaded
to any public server (Fontshare EULA); `fonts/satoshi/README.md` documents the official
Fontshare endpoints and the page's install.sh fetches the faces from them automatically.
Full rules: `fonts/LICENSES.md` on the page.
- `reference/` - byte-exact copies of the generator, template, font installer, and guards
## 1. The five schemes
| slug (storage key) | product label | character |
|---|---|---|
| `studio` | **Adom Studio** | THE DEFAULT (John 2026-07-27; this design briefly staged as 'Adom Studio v2', then took over the name — zero-migration rename). VS Code's Dark 2026 architecture re-grounded on the brand: body #161b22, base text #c9d1d9, every gray on the brand slate hue at 2026's exact contrast steps, product blue re-issued as Adom teal, syntax untouched. Generated by `reference/gen-adom-studio-v2.mjs` from `reference/reference-2026-dark.json`. The old template-projected Studio is abandoned. |
| `studio-bright` | Adom Studio Dark (Brighter Text) | Everything on the deep ground, brighter interface text. |
| `studio-dark` | Adom Studio Dark | The original screen-tuned dark set, quieter text. This is the TOKEN BASE. |
| `kickstand` | Adom Kickstand | The design firm's identity palette: #191919 ground, muted navy chrome, headline face in prose. |
| `slate` | Adom Slate | Calm cool blue-grey; uses the code font Windows already ships. |
**Invariant history:** the old "Adom Studio == Adom Studio Dark (Brighter Text) with `--bg`/`--surface`
traded" parity guard applied to the ABANDONED template-projected Studio and is retired with
it. The new invariant for Adom Studio (since 2026-07-27) is: it is a pure function of
`reference/reference-2026-dark.json` + the two brand anchors, regenerated by
`reference/gen-adom-studio-v2.mjs` — never hand-edited. `check-scheme-parity.sh` still
guards the APP schemes (studio vs studio-bright tokens in styles.css); it no longer
constrains the VS Code Studio theme.
## 2. The tokens, per scheme (generated from styles.css, do not hand-edit)
| token | Adom Studio | Adom Studio Dark (Brighter Text) | Adom Studio Dark | Adom Kickstand | Adom Slate |
|---|---|---|---|---|---|
| `--bg` | `#161b22` | `#0d1117` | `#0d1117` | `#191919` | `#17212e` |
| `--surface` | `#0d1117` | `#161b22` | `#161b22` | `#202127` | `#202a37` |
| `--elevated` | `#1c2128` | `#1c2128` | `#1c2128` | `#282a30` | `#28323f` |
| `--overlay` | `#21262d` | `#21262d` | `#21262d` | `#0e1c31` | `#313b48` |
| `--border` | `#30363d` | `#30363d` | `#30363d` | `#3d3f46` | `#3d4754` |
| `--text` | `#e6edf3` | `#e6edf3` | `#e6edf3` | `#f5f7f9` | `#f0f6fc` |
| `--text-body` | `#c9d1d9` | `#c9d1d9` | `#c9d1d9` | `#d7dde3` | `#d3e0ef` |
| `--text-2` | `#c9d1d9` | `#c9d1d9` | `#8b949e` | `#97a0aa` | `#93a6bd` |
| `--text-3` | `#6e7681` | `#6e7681` | `#484f58` | `#5a626b` | `#4a5a70` |
| `--accent` | `#00b8b1` | `#00b8b1` | `#00b8b1` | `#00b8b1` | `#00b8b1` |
| `--accent-hover` | `#00d4cb` | `#00d4cb` | `#00d4cb` | `#00d4cb` | `#00d4cb` |
| `--accent-bright` | `#00e6dc` | `#00e6dc` | `#00e6dc` | `#8fd3d4` | `#5fe6de` |
| `--on-accent` | `#05221f` | `#05221f` | `#05221f` | `#003c3f` | `#04211f` |
| `--blue` | `#64abff` | `#64abff` | `#64abff` | `#64abff` | `#64abff` |
| `--blue-fill` | `#0061ef` | `#0061ef` | `#0061ef` | `#0061ef` | `#0061ef` |
| `--purple` | `#8c6bf7` | `#8c6bf7` | `#8c6bf7` | `#8c6bf7` | `#8c6bf7` |
| `--green` | `#3fb950` | `#3fb950` | `#3fb950` | `#3fb950` | `#3fb950` |
| `--red` | `#f85149` | `#f85149` | `#f85149` | `#f85149` | `#f85149` |
| `--yellow` | `#d29922` | `#d29922` | `#d29922` | `#d29922` | `#d29922` |
Rationale you must not undo:
- Body text lives in the 9-13:1 contrast band. Below strains; above ~14:1 dark-mode text
haloes (that is why Adom Studio's primary text is 13.0:1, not 15.8:1).
- The teal accent must stay >= 6:1 on every ground. That constraint is why there is no teal
or purple GROUND: they were built and measured into failure.
- Five distinct dark grounds are impossible inside those constraints (closest achievable pair
measured 24 apart, i.e. invisible). The family therefore differentiates on AXES: hue
(Slate), elevation (Adom Studio), legibility (Bright), accents (Kickstand).
- `--blue-fill` (#0061ef) is 3.6:1 on dark grounds: FILLS AND ACCENTS ONLY, never text.
Links and informational text use `--blue` (#64abff).
- `--green/--red/--yellow` are semantic (success/destructive/warning) and nothing else.
A settings confirmation is not "success"; use the accent.
- Kickstand's chrome navy is a DERIVATION, not a new colour: mix(#00204f 45%, #191919 55%)
= #0e1c31. Full-strength navy as a dropdown fill read as "that weird blue" and was demoted
to accents only.
- Kickstand's derived chrome greys are COOL-SHIFTED (correction, 2026-07-26): the firm's PDF
specifies only the #191919 Black ground, which stays byte-exact. The greys we derived from
it (`--surface` #202127, `--elevated` #282a30, `--border` #3d3f46) carry a few points of
blue because pure neutral grey beside the blue-tinted sibling schemes and the wiki is
perceived as warm brown (simultaneous contrast; the shipped pixels measured exactly
neutral, and still read "brownish"). Implementers copying Kickstand must copy these
corrected values, not re-derive neutral ones from the ground.
## 3. Token roles (what drives what)
| token | role |
|---|---|
| `--bg` | the app's deepest ground / the editor sheet |
| `--surface` | panels, toolbars, dialogs |
| `--elevated` | hover surfaces, raised cards, and THE ACTIVE TAB (always the raised sheet; mapping tabs to --surface inverts into a black tab in Adom Studio) |
| `--overlay` | menus, dropdowns, tooltips, popovers |
| `--text` | emphasis only (headings, active items) |
| `--text-body` | running text |
| `--text-2` / `--text-3` | secondary / disabled-borders-only (text-3 fails AA as text) |
## 4. Slugs vs labels, and the migration law
Slugs are storage keys (persisted preferences, data attributes, CSS selectors). Labels are
product names, and they are EXACTLY the VS Code theme names, so one look has one name on
every surface.
History that bites: on 2026-07-25 the slug `studio` CHANGED MEANING (it was the dark base,
it is now the flagship). Any store that ever held old values must translate them ONCE under
old semantics, guarded by a version marker, because the string alone cannot say which era
wrote it. HD's pattern: a NEW localStorage key (`hd-color-scheme2`) whose legacy-key read
translates {studio->studio-dark, studio-bright-inverse->studio, inverse->studio,
blueprint->slate, contrast->studio-bright, graphite->slate} then deletes the legacy key;
plus `desktop.scheme_slug_version = 2` on the saved-preference layer. If AD stores scheme
state, implement the same translation once, and NEVER reuse a slug again without bumping the
version and extending the map.
Unknown/retired slug at load time: fall back to `studio` explicitly. Note the base tokens
ARE studio-dark's look, so "no attribute set" silently paints Dark, not the default: always
set the scheme attribute explicitly.
## 5. Fonts (part of the theme, three faces only)
Brand faces: **Familjen Grotesk** (headline), **Satoshi** (body/prose), **JetBrains Mono**
(code, SIL OFL 1.1 - ship the licence with the files). Do not add faces; variety for its own
sake is how a design system becomes a font menu.
Per-scheme pairing (FONTS_FOR_SCHEME):
| scheme | code stack | prose stack |
|---|---|---|
| studio, studio-bright, studio-dark | `'JetBrains Mono', 'Cascadia Code', Consolas, ui-monospace, monospace` | `Satoshi, -apple-system, 'Segoe UI', sans-serif` |
| kickstand | same code stack | `'Familjen Grotesk', Satoshi, -apple-system, 'Segoe UI', sans-serif` |
| slate | `'Cascadia Code', Consolas, 'JetBrains Mono', ui-monospace, monospace` | Satoshi stack |
Delivery, learned the hard way:
- Webfonts must be served from YOUR OWN origin with ABSOLUTE paths. HD's relative
`./adom-fonts/...` URLs 404'd for weeks while a Windows-installed copy silently covered
for them; nothing anywhere admitted it.
- Isolated webviews (the Claude chat panel) do NOT inherit your page's webfonts. Their brand
faces resolve only from WINDOWS-INSTALLED fonts, so the installer must keep shipping
Satoshi (and Familjen) as Windows fonts even when the main app uses webfonts.
- A fallback chain never fails, it silently falls through. Any font UI must display the
RESOLVED family, measured in the document that renders it, not the configured first name.
- VS Code key trap: `chat.fontFamily` is the chat PROSE; `chat.editor.fontFamily` is the
chat's CODE BLOCKS. They read backwards and were once wired backwards.
## 6. The VS Code theme contract
The editor themes are GENERATED from the tokens (HD `tools/gen-vscode-themes.mjs`), one
theme per scheme, labels identical to the picker labels. Rules:
- An unknown `workbench.colorTheme` name silently falls back to VS Code's DEFAULT LIGHT
theme. Renames must therefore update the installed pack AND the live setting in one step,
and a machine-side guard (`check-installed-themes.sh`) should verify the installed pack,
because repo-vs-repo checks cannot see a stale machine.
- The generator wipes its output dir before writing so a renamed theme cannot leave a stale
file behind.
- Current pack: adom.adom-theme (Adom Studio / Adom Studio Dark (Brighter Text) / Adom Studio Dark /
Adom Kickstand / Adom Slate), version-locked to this package: the extension version and
the package version are THE SAME NUMBER, bumped together in the same commit, every time.
- The extension is NEVER in any marketplace/gallery, so gallery update checks return null
for it by design; the only update channel is `adom-wiki pkg update`. Currency check =
installed adom.adom-theme version vs this package version.
- Legacy: before 2026-08-02 this was `adom-theme-system` + `adom.adom-themes` (7.x line);
install.sh purges the old extension id on every install.
### Delivering the pack: install it, never copy it
**Copying the pack folder into `~/.local/share/code-server/extensions/` does not
install it.** The folder is absent from that directory's `extensions.json`, and
code-server PRUNES unregistered folders on its next launch. The pack disappears
before the user ever sees it, the picker lists zero "Adom ..." themes, and a
`workbench.colorTheme` of "Adom Studio" then hits the unknown-name rule above and
falls back to stock Light. This shipped in a golden image and rendered the whole
editor off-brand; `install.sh` now packages a `.vsix` and installs it properly.
Three traps worth knowing before you write your own installer:
1. **`code-server --install-extension` takes a `.vsix`, not a directory.** Build one
at install time: it is a zip of `extension/<the pack>` plus `extension.vsixmanifest`
and `[Content_Types].xml`. No `vsce`, no network, python's `zipfile` is enough.
2. **The `code-server` on `PATH` inside an editor terminal is the remote-cli shim**
and cannot install anything. Resolve the real launcher (`/usr/lib/code-server/bin/
code-server` and friends) and reject any path containing `remote-cli`.
3. **The CLI dies with `error not spawned with IPC`** when it inherits
`CODE_SERVER_PARENT_PID` (and `VSCODE_IPC_HOOK_CLI`) from the editor terminal that
is running the installer. `env -u` both before invoking it.
The litmus is registration, not files on disk: assert the `adom.adom-themes` id
appears in `extensions.json` after installing. A directory check passes for a pack
that is about to be deleted, which is exactly how this got past a build gate.
Uninstall through the CLI too, so the registration goes with the folder.
## 7. Implementing in Adom Desktop, the checklist
1. Define the tokens as CSS custom properties on `:root` (the studio-dark values) plus one
`[data-scheme='<slug>']` block per scheme copied from the table above. Adom Studio's
block should contain ONLY Bright's text ramp plus the bg/surface swap.
2. Route every colour through the tokens. No hardcoded hex: HD carried 4,641 hardcoded
values before the sweep and that is why its panels never matched each other.
3. Picker UI: show the LABELS, store the SLUGS, default `studio`, apply via the data
attribute before first paint (read the cache directly, do not wait for stores).
4. Fonts follow the scheme's pairing; if you expose font pickers, overrides beat the
pairing, "Match theme" means follow it, and the UI must name what that currently means.
5. Copy the guards: scheme parity, brand-teal component matching (one colour has at least
five CSS spellings; match rgb components, not strings), installed-theme verification if
you install editor themes.
6. Do not silently restyle approval/security surfaces with scheme colours that weaken
semantics: amber stays the heavy-action colour, red stays destructive.
## Version
Spec v1.0.0, extracted from hydrogen-desktop @ 2026-07-26 (pack v7.1.0). When HD's tokens
change, regenerate section 2 from styles.css rather than editing it by hand.