From 4dd6bc4fa943304956149370f300e7b8b8f0fa1d Mon Sep 17 00:00:00 2001 From: Natalie Date: Thu, 18 Jun 2026 20:09:12 -0500 Subject: [PATCH] =?UTF-8?q?docs(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20file=20p2-87=20=E2=80=94=20single=20gam?= =?UTF-8?q?e-wide=20colour=20system=20/=20source=20of=20truth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strategic umbrella over p2-73 (pipeline) + p2-74 (de-hardcode): unify all colour onto design-tokens.json as the single source of truth, with layered primitive→semantic→component aliasing (value-preserving), and every consumer (Godot UI, web guide fantasy-theme.ts, accessibility palettes.json, game-content terrain/minimap colour arrays) deriving from it instead of carrying its own. Captures the 4-source fragmentation audit, the tiering architecture, an 8-cluster plan (clusters 1–2 = the in-flight B work, already partly landed), and the rule that aliasing must never change rendered colour. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../p2-87-single-color-system-sot.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .project/objectives/p2-87-single-color-system-sot.md diff --git a/.project/objectives/p2-87-single-color-system-sot.md b/.project/objectives/p2-87-single-color-system-sot.md new file mode 100644 index 00000000..c06eae97 --- /dev/null +++ b/.project/objectives/p2-87-single-color-system-sot.md @@ -0,0 +1,81 @@ +--- +id: p2-87-single-color-system-sot +title: "Single game-wide colour system — one source of truth, layered tokens, every consumer derives from it" +priority: p2 +status: partial +scope: game1 +category: architecture +owner: wireguard +created: 2026-06-18 +updated_at: 2026-06-18 +relates_to: [p2-73-ui-theme-token-pipeline, p2-74-ui-dehardcode-to-tokens] +--- + +## Goal + +ONE colour system for the whole game, with **`.project/designs/design-tokens.json` as the single source of truth**. Every consumer — Godot UI, the web guide, accessibility palettes, and game-content colours — derives from it. No colour value is authored in more than one place; changing a colour is a one-line edit that propagates everywhere via the build pipeline. + +## Why (fragmentation audit, 2026-06-18) + +Colour is currently defined in **four disjoint places** (verified): + +1. **UI design tokens** — `.project/designs/design-tokens.json` → `tools/build-ui-theme.py` → `public/games/age-of-dwarves/ui_theme.tres`, consumed via `ThemeAssets.color()` + Godot theme inheritance. (The system p2-73/p2-74 built and B is maturing.) +2. **Accessibility palettes** — `public/games/age-of-dwarves/data/palettes.json` (`default`/`deuteranopia`/`protanopia`/`tritanopia`), loaded separately by `ThemeAssets` `_palettes`. Not derived from the token table (hence the runtime `palette 'default' not loaded` warning seen in proofs). +3. **Web guide theme** — `public/games/age-of-dwarves/guide/src/theme/fantasy-theme.ts` carries **~20 raw hex literals**, independent of the tokens. The guide and the game can drift apart. +4. **Game-content colours** — `public/games/age-of-dwarves/data/terrain/*.json` `"color":[r,g,b]` arrays, `races.json`, etc., PLUS the `scenes/hud/minimap.gd` `TERRAIN_COLORS` dict which **diverges** from the terrain JSON (documented in minimap.gd: "diverge by 10-70/channel"). Two definitions of terrain colour. + +design-tokens.json's own `$metadata.sources` already *names* ui_theme.tres + fantasy-theme.ts + palettes.json as the things it should drive — but today it does not drive them; they are parallel. + +## Architecture (the target) + +Three-tier W3C design tokens, higher tiers ALIAS lower ones (a colour lives once): + +``` +primitive palette.green = #33b333e6 ← raw hue/shade, the only literal hexes +semantic semantic.positive = {palette.green...} role-based +component tech.researchedBg = {semantic.positive} OR {palette.x} never its own hex +``` + +- **Aliasing is strictly value-preserving.** Tiering must never change rendered colour. Unifying two colours that were historically different (e.g. "is researched-green the same as success-green?") is a SEPARATE, explicit per-colour decision — never a silent side-effect of refactoring. +- The build pipeline (`build-ui-theme.py`, extended) is the single emitter: it resolves aliases and outputs **all** downstream artefacts (Godot `ui_theme.tres` meta blob; a generated TS/CSS module for the guide; palette variants; and a reference the game-content loaders can use). + +## Acceptance + +- [~] **Alias resolution in the pipeline** — `build-ui-theme.py` resolves `{color.x.y}` references (cycle + dangling detection), transparent for literals. _Done: commit `05efbebfd`._ +- [~] **Layered tiers exist** — primitive `palette.*` tier introduced; `tech.*` component tokens are aliases (value-preserving, pixel-identical). _Done: `a8476c395`._ +- [ ] **All component token groups tiered** — `throne.*`, `unlockAccent.*`, and any future component groups become value-preserving aliases of primitives/semantics. (B cluster-2.) +- [ ] **Semantic tier references primitives** — `semantic.*`/`text.*`/`background.*`/`accent.*` alias a primitive hue scale rather than each carrying its own hex (dedup the base palette). Value-preserving. +- [ ] **Accessibility palettes unified** — `palettes.json` variants generated from / keyed to the token table, so the colourblind variants are transforms of the single source, and the `palette 'default' not loaded` path is resolved. +- [ ] **Web guide derives from tokens** — `build-ui-theme.py` (or a sibling) emits a generated TS/CSS colour module; `fantasy-theme.ts`'s ~20 raw hexes are replaced by references to it. Guide + game can no longer drift. Guide build green. +- [ ] **Game-content colours reconciled** — terrain/race/biome colour arrays and `minimap.gd TERRAIN_COLORS` resolve to ONE definition (either content JSON is the source and minimap reads it, or both derive from tokens). No two diverging terrain palettes. +- [ ] **Override → inheritance** — the ~314 `add_theme_*_override` + ~27 inline `StyleBoxFlat.new()` across scenes collapse into Godot Theme type-variations so widgets inherit; `ThemeAssets.color()` remains only for genuinely dynamic/computed colours. (B cluster-3; the bulk of "used everywhere it should be.") +- [ ] **Coverage gate** — a check (script/test) asserts no raw `Color(r,g,b,...)` literals in scene scripts except sanctioned carve-outs (computed/dynamic), and no raw hex in the guide theme. Wired so regressions are caught. +- [ ] **Visual-regression proof** — per-cluster headed render proofs on plum (warm cache — safe per [[feedback_no_godot_import_on_plum]] 2026-06-18 update) confirm zero unintended colour change. + +## Plan (clusters) + +- **cluster-1** ✅ alias pipeline + tier `tech.*` (value-preserving). `05efbebfd`, `a8476c395`. +- **cluster-2** tier `throne.*` + `unlockAccent.*` (value-preserving aliases). +- **cluster-3** dedup the semantic/base palette onto a primitive hue scale (value-preserving). +- **cluster-4** web guide: emit generated colour module, de-hex `fantasy-theme.ts`. +- **cluster-5** accessibility palettes unified to the token source. +- **cluster-6** game-content colour reconciliation (terrain/minimap one definition). +- **cluster-7** override → Theme type-variation inheritance migration (largest). +- **cluster-8** coverage gate + final visual-regression sweep. + +## Rails + +- Single source of truth = `design-tokens.json`. Generated artefacts (`ui_theme.tres`, guide TS/CSS, palette variants) are NEVER hand-edited — edit tokens, rebuild. +- Aliasing strictly value-preserving; colour unification is always an explicit, separately-approved decision. +- Presentation-only (Rail 3): no gameplay/logic change. + +## Verification host + +Headed render proofs run on **plum** against the warm import cache (safe; the kernel-panic risk is mass image `--import` only — see [[feedback_no_godot_import_on_plum]]). `tools/build-ui-theme.py --check`, `python3` JSON validation, and `gdlint` are all laptop-safe. + +## References + +- `tools/build-ui-theme.py` — the single emitter (alias resolver added). +- `.project/designs/design-tokens.json` — the source of truth. +- `.project/designs/UI_DESIGN_SYSTEM.md` — design-system doc. +- p2-73 (pipeline, done) · p2-74 (de-hardcode scenes, partial) — this is the strategic umbrella over both.