Repository navigation
Consolidate autobot-frontend's three base design-token sources #14785
Description
Part of #14774. Pairs with #14775 — that issue converges the vocabulary across packages; this one converges it within autobot-frontend.
Problem
autobot-frontend defines its base design tokens in three loaded files plus one unloaded copy. --bg-primary, --text-primary and --border-default are each declared in 8 files.
Loaded base sources:
| File | --var: declarations |
Loaded via |
|---|---|---|
src/assets/css/design-tokens.css |
678 | src/assets/css/index.css → src/main.ts |
src/assets/styles/theme.css |
192 | src/main.ts directly |
src/assets/tailwind.css @theme block |
~95 aliases | src/main.ts |
Per-theme overrides (legitimate, keep): css/themes/light.css, dark.css, accents.css, ember.css, _template.css, plus src/assets/aui-theme.css (49 vars).
An unloaded duplicate: src/assets/tokens.css declares 106 tokens but is imported by nothing — css/index.css imports ./design-tokens.css, not this file. Its own header says:
Master Design Tokens: src/assets/css/design-tokens.css
This file: src/assets/tokens.css (reference)
So it is a hand-maintained mirror that never reaches the browser, while reading as authoritative to anyone who opens it. Divergence between it and the master is silent and undetectable.
Why this blocks the umbrella
#14775 converges the token vocabulary across libs/autobot-ui, autobot-frontend and autobot-slm-frontend. That convergence cannot be verified or held while one package has three live definition sites and a fourth that looks live. Any guard test written for #14775 would have to pick one source and would silently miss drift in the others.
Approach
- Establish the precedence order that exists today — which file wins for a token declared in several. Load order is
tailwind.css→styles/theme.css→css/index.css; confirm empirically rather than by reading, since later declarations win. - Pick one base source.
css/design-tokens.cssis the self-declared master and has the most tokens. - Reconcile
styles/theme.css— its 192 tokens either move into the master or are demonstrated to be a distinct layer (it is also the fileuseThemeVariant.tsreads). Document which, do not merge blindly. - Resolve
src/assets/tokens.css. Diff it against the master first. Any token it defines that the master lacks is a real token that must be promoted — this is unfinished work to wire in, not dead weight to drop. Only once it is a proven subset does it become a redirect or get retired. - Add a guard test asserting exactly one base declaration site per token name, with per-theme override files exempt.
Risks / constraints
- Token consolidation is a visual-regression risk across 451 components.
npm run test:visualbaselines must be reviewed, not blanket-updated. useThemeVariant.tsandusePreferences.tsread token files directly — check both before moving anything.- Do not delete
tokens.cssbefore step 4 proves it is a subset.
Acceptance criteria
- Written precedence order for the loaded token sources, verified empirically
- One base declaration site per token name; per-theme files hold overrides only
-
src/assets/tokens.cssdiffed against the master; any unique tokens promoted, with the diff recorded in the PR - Guard test fails on a second base declaration of any token name
- Visual snapshots reviewed; no unintended diffs
Activity
mrveiss commentedon Sep 26, 2026
Re-measured on
mainat12563bc052before starting. The three loaded sources hold up. The fourth — the "unloaded duplicate" — is not what the body describes, and the difference changes the work.src/assets/tokens.cssdeclares nothingThe body says it "declares 106 tokens" and calls it a hand-maintained mirror. Measured:
--character sequencesThe whole file is one commentary block. Every
--tokenin it is prose inside/* ... */, listing names and pointing at the real definitions:The "106 tokens" figure is a count of names mentioned in comments, not of declarations. A regex for
--name:matches prose in a comment exactly as it matches a declaration, which is how the same file reads as 106 declarations and as zero.Why this matters rather than being a footnote
It is not a duplicate token source. It cannot shadow, drift into, or override anything, because nothing in it reaches a parser even if it were imported. Merging it into
design-tokens.css, or diffing its values against the live set, would be work against a file with no values.It is a stale document asserting canonicality. Its header calls it the "Design Tokens Canonical Reference" and says "Components should import design-tokens.css which includes this file" — and
css/index.css:17imports./design-tokens.css, which does not include it. So a file that names itself canonical is unreferenced, and the sentence describing how it is wired in is false.That is the same defect this issue is about, one level up: the canonical source is asserted in a comment rather than declared anywhere a tool can read. It belongs to #17571's declaration work, not to a token-merge.
The real consolidation is unchanged and is still three files —
css/design-tokens.css(678),styles/theme.css(192),tailwind.css@theme(~95 aliases). The--bg-primary/--text-primary/--border-default-in-8-files observation stands; I have not re-derived it.Suggested change to the checklist
Method note
I nearly reported the opposite. My first parse returned
0 tokensand I treated that as a failed parse rather than a result, because a 0 from a regex is usually a broken regex — then checked the file and found the 0 was real. Worth stating because the same check run on the same file gives 106 or 0 depending on whether comments are stripped, and only one of those answers the question the issue is asking.docs(research): dashboard shell and GUI-element standardisation audit (…
docs(research): dashboard shell and GUI-element standardisation audit (…
fix(theme): one themed source for notification colour, and a ratchet …
fix(canonical): the audit harness reported clean over a tree it never…
feat(canonical): wire the frontend audit into CI and let a blocking r…
6 remaining items
feat(design): contrast and mono themes in useTheme and the user store (…
feat(design): the Refined SLM scheme, keyed on data-app, with the sha…
fix(theme): ember stops being the default palette, and density scales…
refactor(design-tokens): one adapter in the kit, one base token file (#…
feat(theme): Light is the default theme and the theme's own primary i…
fix(theme): body-text links keep a non-colour cue, and ember cannot r…
fix(canonical): parse the token tree once per run, and give the whole…
fix(i18n): theme and accent labels and announcements are translated i…
fix(theme): ember cannot repaint contrast or mono, light is set befor…
test(guards): the cleared --indigo-800 site leaves the undefined-var …
test(canonical): the unloaded-authority rule tests walk the tree, so …
fix(frontend): tokenise preset-picker and showcase swatch colours, re…
fix(types,i18n): regenerate user-preferences API types and re-lift SL…
fix(i18n): the Persian theme-toggle System label reuses the existing …
refactor(theme): density reuses the existing [data-layout-density] at…