Decision 0021
One token file generates every surface
Architecture decision record 0021: One token file generates every surface
Context
Maximum beauty with minimal design means one source of truth and nothing hand-tuned per surface.
The three plans
- Grok: dim phosphor, one accent, no neon soup; 16-colour capable; fewer themes than docs.
- Fable: one config drives Hyprland, waybar, terminal, GTK, Neovim; six to eight curated themes; one mono and one sans.
- Codex: minimal, quiet, dense when needed, dark and light, animation sparingly; avoid gamer styling, hacker green, glassmorphism, giant chat windows.
Decision
design/tokens.toml (colour scales, one accent, type scale, spacing, radii, motion) generates the
TUI theme, the Iced theme, the terminal palette, the compositor’s configuration fragment, and
palette-level GTK themes (that toolkit honours no more). Inter for interface text, JetBrains Mono
for code and the TUI; a proper icon set, no patched fonts.
Two faces are what this machine writes in; they are not all the text on it. Inter covers Latin,
Greek and Cyrillic, and with only those two installed a browser — the main application on this
desktop — drew a row of boxes for every emoji, every Chinese name and every Arabic street sign.
So Noto sits behind them and never in front: it is designed to be the face nothing else covers,
which is exactly the job. Not fonts.enableDefaultPackages, which is the other way to fix it and
the wrong one — DejaVu, Liberation and FreeFont are substitutes for other people’s fonts, and
installing four families that each claim to be a general sans puts them into fontconfig’s matching
against the two this machine chose. “No font soup” is about what wins a match, not about how few
fonts are on the disk.
What “palette-level” means for GTK is @define-color, which is the one override point both halves
of it document: GTK 3’s Adwaita is built on theme_bg_color and its neighbours, libadwaita on
window_bg_color and its neighbours, and redefining those recolours an application without a
single widget rule. A stylesheet of selectors would restyle more and would stop being true on the
next release of anything, silently, in a file nobody reads. Beside it goes GTK’s other mechanism,
settings.ini, which carries no colour at all: the typeface, the icon set, the pointer, and which
way round the theme goes. libadwaita reads neither and asks the desktop portal for
org.gnome.desktop.interface color-scheme, so that is set too, from the same token.
Qt gets no generated theme, which is a correction to this record rather than a thing not yet
done. It reads the same GSettings through the gnome platform theme, so a Qt window follows this
machine out of the one value the rest of it follows. A second generated theme, for a toolkit this
machine ships no application in, would be a file that is correct on the day it is written and
never observed again — which is what this project keeps finding and calling by its name.
The virtual console gets the sixteen too, which is where this claim was thinnest: every
terminal emulator on the machine had them and the console did not, so a tty, the greeter and the
recovery console all drew in the kernel’s default palette. On a server or a board that is not a
corner — it is the whole face. console.colors is set in the module every machine has rather
than in the desktop’s, dark by default because a machine with no screen has no other mode to be
in, and overridden by a machine that declares a light face.
That in turn is what lets the greeter be in the system at all. tuigreet takes a theme as
role-to-colour-name pairs and no hex, and it draws on that console — so naming green there is
naming the accent. It is the one surface here that carries no colour of its own on purpose: a hex
triple in that string would be a second declaration of something that already has one, and it
would go stale the first time the palette moved.
The order the sixteen go in is a token as well, for the same reason the cursor is. A console takes them as a list with no names on it, so a machine handed them in the order a map happens to sort shows a red where its green should be — and nothing anywhere reports that.
The cursor is a token, in the same file as the colours. It is as much a part of a theme as a
border, it is the part a person looks at the whole time, and four readers have to say one word:
the compositor’s environment, GTK’s settings, GSettings, and whatever installs the package. A
pointer that changes shape as it crosses a window is what happens when one of them says it from
memory. Dark and light as equals; motion only when it explains a
state change; no neon, glass, gradients or sci-fi noise. Published budgets: power-on to prompt,
idle memory, tacid diff latency, no-op apply, voice latencies; measured in CI on the reference
laptop.
Consequences
- Themes are token files; contributions cannot introduce hard-coded colours.
- The badges (
NET,AIRGAP,SOVEREIGN,MIC,SELF-BUILT) are part of the design system, not afterthoughts.
Alternatives rejected
- Per-app theming: drift and inconsistency within a month.
- Nerd Font glyphs everywhere: legibility and licensing noise.