Skip to content

Token Engine

Located in src/lib/token-engine.ts. Generation runs entirely in the UI thread (not the sandbox) and produces a flat array of GeneratedToken objects.

Generation Phases

The current pipeline has six main phases (0–5) plus a 2b inv-frame sub-phase, mirrored in src/lib/token-engine.ts (search "Phase N:" inline). Collections use the flat layout under color/... (the collection path is not prefixed with the theme name, under the single-theme-in-Figma policy).

PhaseWhatCollection pattern
0: Metatheme-name / description (only when set) / color/contrast-model / color/collection-structure / color/skip-duplicate-tokens / counts (STRING + FLOAT, theme-level meta), per-entity stats, per-mode stats, and seed-color (per-mode COLOR, color-category). The engine always generates these; the Push meta to Figma toggle (theme.settings.pushIncludeMeta) filters them out at push time.meta, color/meta
1: SurfacesLevel entities from seed color. Condition + interaction segments skipped — surfaces don't iterate either axis.color/{entity}
2: ContainersFrame entities per surface level. Iterates conditions × interactions when the active conditions are interactive.color/{entity}/{surface}-{level}
2b: Inv-containersSame frames computed with flipped direction. Merged into the same collection as Phase 2 with an inv/ prefix on the variable path (no separate inv- collection).color/{entity}/{surface}-{level} (paths gain inv/ prefix)
3: ContentTop-layer entities on surfaces, containers, and inv-containers. Both directions; inverse merged with inv/ prefix.color/{entity}-on-{parent}/{surface}-{level}[/{containers}-{level}]
4: ScrimsOverlay entities from seed color. Condition + interaction segments skipped.color/{entity}
5: Fixed coloursMode-invariant flavour for frame / top-layer entities when Enable Fixed colors is on. Synthetic single-mode collection.color/fixed/{entity}

Token Path Structure

text
color/{collectionName}/{conditionName?}/{interactionName?}/c-{contrastIndex}/{colorId}
  • Static condition (e.g. Disabled, interactive: false) → no interaction segment: color/{entity}/disabled/c-N/color-name.
  • Interactive condition (e.g. Enabled) → both segments: color/{entity}/enabled/{interactionName}/c-N/color-name.
  • Inverse tokens add an inv/ prefix anywhere along the path.
  • Surfaces / scrims skip only the interaction segment — they still emit a condition segment (always present, even for the lone seeded Enabled): surface color/surfaces :: enabled/surfaces-0/color-name; scrim color/scrims :: scrims-0/enabled/c-N/color-name.

Deduplication

Two levels of deduplication:

  1. Generation-time -- within each (condition, interaction) group, if a token produces identical RGBA values across all modes as a previously generated token with the same dedup key, it is skipped. The count is tracked in skippedDuplicates. The Skip duplicate tokens switch in Color Settings (default on) controls this.
  2. Push-time (FNV-1a hash) -- each collection's token set is hashed (paths + per-mode values + each token's aliasOfTokenId). If the hash matches the stored hash from the previous push (systema_hash plugin data on the collection), the entire collection is skipped with no Figma writes. Including the alias state means toggling Alias duplicate tokens still triggers a re-push.

A separate, opt-in aliasing pass (assignAliases, default off — settings.aliasDuplicateTokens) runs after generation: tokens with an identical per-mode hex signature inside the same collection collapse onto the first canonical token via aliasOfTokenId, which becomes a Figma VARIABLE_ALIAS on push. STRING/meta tokens and tokens without a tokenId are never aliased.

The Skip low-contrast tokens switch (default on) is a third filter — independent from dedup — that prunes tokens whose actual contrast against parent falls below the entity's smallest contrast step in EVERY mode (direct AND inverse axis). See Skip low-contrast tokens above.

surfaceScope Filtering

For frame and top-layer entities, the surfaceScope property (all, first, last) determines which surface level indices are included in generation, reducing output for entities that only need to appear on specific levels.

Disabled Filtering

The following are excluded from generation:

  • Colors with enabled === false
  • Conditions with enabled === false (note: Enabled condition is undeletable, but can be disabled to suppress its tokens)
  • Interactions with enabled === false
  • Modes with enabled === false

Collection structure

settings.collectionStructure (default balanced) decides how the flat token array is bucketed into Figma variable collections:

  • compact — everything in a single color collection. The per-mode color/meta (seed-color) and theme-level single-mode meta merge so the user sees color + meta only.
  • balanced (default) — grouped by phase: color/surfaces (scrims merge in here), color/containers, color/content, plus color/meta and theme-level meta.
  • granular — per-surface-level collections (e.g. color/{entity}, color/{entity}/{surface}-{level}) to stay under the 5,000-variable-per-collection Figma limit on large themes.

All three produce genuinely different layouts, so switching between any pair after a push triggers the cross-collection migration flow (create new variables → rebind node refs via systema_tid → legacy-alias the old ones). The Fixed flavour always lives in its own single-mode color/fixed/{entity} collections regardless of structure.

Variable-count estimation

estimateVariableCount(theme, preGeneratedTokens?, preSkipped?) tallies tokens per collectionName and returns { collections, totalVariables, totalModes, skippedDuplicates }. totalVariables is tokens.length; totalModes uses effectiveModes (so the synthetic Value placeholder counts as 1); each collection is flagged withinLimit against FIGMA_LIMITS.variablesPerCollection (5,000). The header pill and collection grid read this estimate.