TacidOS
Developers
Documentation

Decision 0021

One token file generates every surface

Architecture decision record 0021: One token file generates every surface

Status
Accepted
Date
2026-09-03

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.