Skip to main content

Build Pipeline

Premise

The Aplica Tokens Theme Engine build pipeline transforms configurations into consumable tokens for all platforms. It is a process in two major stages: data generation (config → data/) and output transformation (Style Dictionary: data/dist/).

Understanding the pipeline is essential for those who maintain themes, add new colors, or need to diagnose why a token is not appearing in the output.


Flow Overview

theme-engine/config/*.config.mjs


[ Stage 1: Data Generation ]

├─ themes:generate → data/brand/<theme>/
├─ dimension:generate → data/dimension/normal.json
├─ sync:architecture → data/mode/, data/surface/, data/semantic/, data/foundation/
├─ foundations:generate → data/foundation/<name>/styles/
└─ figma:generate → data/$themes.json, data/$metadata.json, data/$themes.engine.json.template


[ data/ ] ← NEVER edit manually


[ Stage 2: Style Dictionary Build ]

npm run tokens:build:all


[ dist/ ]
├── json/ ← JSON with px (Figma, Tokens Studio)
├── css/ ← CSS custom properties with rem
├── esm/ ← ES Modules with px
├── js/ ← CommonJS with px
└── dts/ ← TypeScript declarations

Stage 1 — Data Generation

themes:generate — Color Decomposition

The main script for the data stage. For each *.config.mjs in theme-engine/config/:

  1. Reads colors and mapping from the config
  2. For each declared color, calls ColorDecomposer which:
    • Converts hex → OKLCh
    • Interpolates lightness to 19 palette levels (10–190)
    • Generates 15 neutral levels (5–140) with chroma reduced to 10%
    • Calculates txtOn (WCAG AA/AAA) and border for each level
    • Inverts the scale for dark mode (level N → level 200-N)
  3. Generates _typography.json via TypographyGenerator
  4. Generates _borders.json with references to the dimensional scale

Files produced per theme in data/brand/<theme>/:

FileContents
_primitive_theme.jsonRaw decomposed palettes (palette + neutrals + behavior per color)
_grayscale.jsonFixed grayscale scale (15 levels, customizable via override)
_brand.jsonSemantic mapping: theme.color.light.* and theme.color.dark.*
_typography.jsonTypography tokens (families, weights, sizes, line-heights)
_borders.jsonBorder radius with references to the dimensional scale
_ui.jsonComponent UI tokens (only when options.uiTokens: true)

sync:architecture — Reference Propagation

The most critical and least obvious script in the pipeline. Reads the architecture schema — managed by the package — and propagates the correct references to all intermediate layers.

What it writes (overwriting any manual edits):

FileWhat is propagated
data/mode/light.jsonRefs from theme.color.light.* for feedback, function, ambient
data/mode/dark.jsonRefs from theme.color.dark.* with scale inversion
data/surface/positive.jsonRefs from mode for positive context
data/surface/negative.jsonRefs from mode with inverted scale (photographic negative)
data/semantic/default.jsonComplete semantic tokens referencing surface
data/foundation/engine/default.jsonFoundation aliases referencing semantic

Fundamental rule: data/mode/, data/surface/, data/semantic/, and data/foundation/engine/ are generated files. Any manual edit is overwritten on the next sync execution.

How surface and mode relate

data/surface/positive.json
│ references

data/mode/light.json ←─── (or dark.json, depending on the bundle)
│ references

data/brand/<theme>/_brand.json

The surface does not know whether it is light or dark — it references the mode. The mode selects which brand file to use. The light/dark choice happens at build time, when $themes.json defines which combination of files is merged.

foundations:generate — Foundation Styles

Generates the composed style files — typography styles and elevation styles — which are not primitive tokens, but pre-composed CSS classes directly consumable in Figma and code:

data/foundation/<name>/
├── default.json ← alias tokens (foundation.bg.primary → semantic.color.*)
└── styles/
├── typography_styles.json ← composed typographic styles (.typography-heading-title_1)
└── elevation_styles.json ← shadow classes per level (.elevation-level_two)

figma:generate — Figma and Tokens Studio Scaffolding

Generates (or merges) the three files Tokens Studio reads to understand which token sets belong to which theme variant:

FilePurpose
data/$themes.jsonActive theme entries imported by Tokens Studio. Preserves Figma-owned fields (id, $figmaStyleReferences, variable IDs) on merge.
data/$themes.engine.json.templateEngine-owned canonical template — same structure as $themes.json with empty Figma fields. Used as a reset reference.
data/$metadata.jsonToken-set load order for the active workspace.

The generator rebuilds the structural entries (selectedTokenSets, name, group) from the active workspace configuration. When data/$themes.json already exists, it preserves any Figma-assigned IDs and style references so that Figma syncs survive regeneration.

Do not delete these files. If data/$themes.json is deleted, all Figma style references stored in it are lost and must be re-synced from Figma.

This command runs automatically as part of theme-engine build. Run it standalone when you only changed the workspace structure (added or renamed a theme, surface, or mode) without changing token values:

theme-engine figma:generate

Stage 2 — Style Dictionary Build

Style Dictionary v5 reads data/ and the $themes.json configuration file, resolves all references ({semantic.color.brand.first.default.background}), applies platform transformations, and writes the files to dist/.

The $themes.json file

Defines which combinations of token files are merged to generate each theme variant:

{
"aplica_joy-light-positive": {
"selectedTokenSets": {
"brand/aplica_joy/_brand": "enabled",
"brand/aplica_joy/_typography": "enabled",
"brand/aplica_joy/_grayscale": "enabled",
"brand/aplica_joy/_borders": "enabled",
"mode/light": "enabled",
"surface/positive": "enabled",
"semantic/default": "enabled",
"foundation/engine/default": "enabled",
"dimension/normal": "source"
}
},
"aplica_joy-dark-positive": { /* same structure, mode/dark */ }
}

Each entry in $themes.json = one output file in dist/. This file is generated by figma:generate — do not edit manually.

Reference Resolution

Style Dictionary traverses all chained references and resolves them to final values:

foundation.bg.primary
→ {semantic.color.interface.function.primary.normal.background}
→ {surface.interface.positive.function.primary.normal.background}
→ {mode.interface.positive.function.primary.normal.background}
→ {theme.color.light.interface.positive.function.primary.normal.background}
→ #C40145 (final value for aplica_joy light)

Platform Transformations

Each platform applies specific transformations before writing:

PlatformMain transformationOutput unit
jsonResolves references, maintains nested structurepx
cssGenerates --semantic-* and --foundation-*, converts dimensionsrem
esmGenerates ES module with exported objectpx
jsGenerates CommonJS modulepx
dtsGenerates TypeScript declarations

Important exception: Tokens with $type: "number" (e.g., semantic.depth.spread) are never converted to rem — they remain in px on all platforms. See 05-output-formats.md for the full list of exceptions.


The Architecture Schema

The architecture schema is the single source of truth for token structure. It defines:

  • Which feedback types exist (info, success, warning, danger)
  • Which variants each feedback has (default, secondary)
  • Which product items exist (promo, cashback, premium)
  • Which intensity levels the semantic layer exposes (lowest, default, highest)

The schema is owned by the package. Consumers can inspect the active schema and override specific parts by placing schema files in theme-engine/schemas/.

# View current schema
theme-engine sync:architecture:schema

# Verify impact without writing
theme-engine sync:architecture:test

When the schema changes (e.g., adding a new feedback item), sync:architecture propagates that change to all layers. Themes that do not declare the new color in mapping will receive a warning during the build.


Full Pipeline vs Incremental

Full pipeline — use after clone or broad changes

npm run tokens:build

Runs in order:

  1. ensure:data — creates necessary directories in data/
  2. dimension:generate — generates dimensional scale
  3. themes:generate — decomposes colors for all themes
  4. sync:architecture — propagates references between layers
  5. foundations:generate — generates foundation styles
  6. figma:generate — generates Tokens Studio / Figma scaffolding files
  7. build:all — Style Dictionary → dist/

Incremental builds — use for targeted changes

ChangeRequired commands
Change a theme's colortokens:themestokens:build:all
Change dimensional scaletokens:dimensiontokens:build:all
Change schema (add feedback/product)tokens:synctokens:themestokens:build:all
Change foundationtokens:foundationstokens:build:all
Add / rename theme, surface, or modetokens:figma
Rebuild only (data/ intact)tokens:build:all

The gradients trap

Gradients require special attention to order:

themes:generate → generates _brand.json WITH gradient

sync:architecture → propagates gradient up to semantic.color.gradient

build:all → emits CSS gradient variables

If sync:architecture does not run after themes:generate, the semantic.color.gradient section does not exist and build:all warns (without failing) and omits gradients from the output. Solution: always use tokens:build (full pipeline) or run sync manually before the build.


Visual Preview (since 3.9.0)

After the Style Dictionary build, theme-engine preview generates a workspace-local static HTML report that renders every generated theme combination — before any Figma sync or browser integration.

# Generate preview from the current dist/ output
theme-engine preview

# Rebuild dist/ first, then generate the preview
theme-engine preview --build

# Rebuild, generate, and serve via a local static server with live reload (since 3.11.0)
theme-engine preview --build --serve

Since 3.11.0, --serve adds live reload: the browser tab refreshes automatically whenever dist/ changes. This enables a tight feedback loop — run npm run themes:generate && npm run build in a separate terminal, and the preview updates instantly without manual reloads.

What the preview renders:

SectionWhat it shows
Colorsbackground, border, txtOn, txt across every semantic family, state, and intensity level
TypographyAll foundation typography classes applied to live sample text
ElevationAll elevation classes rendered as raised card surfaces

Output written to:

dist/preview/
├── index.html ← open in the browser
├── preview-data.js ← resolved token data
├── preview-app.js ← renderer
└── preview-app.css ← preview styles

When to use it:

  • After adding a new theme — confirm all four variants (light/dark × positive/negative) render correctly.
  • After changing a color or override — visually verify that txtOn contrast and txt canvas colors are correct before syncing to Figma.
  • When validating ghost surfaces — ghost.normal.background is transparent, so checking the effective composited canvas requires a visual tool.

Add to the incremental build table:

ChangeRequired commands
Inspect output visuallytheme-engine preview (or theme-engine preview --build to rebuild first)

The preview renders against the current dist/ output. Run with --build to ensure dist/ is up to date before opening.


Build Validation

The pipeline includes automatic checks:

  • Broken references: If a token references another that does not exist, the build fails with a resolution error
  • WCAG contrast: The engine reports AA/AAA failures as warnings during themes:generate. Configure accessibilityLevel and acceptAALevelFallback in the theme options to control behavior.
  • Schema structure: sync:architecture:test validates whether theme configs align with the schema without writing anything
  • Data integrity: Run theme-engine validate:data before publishing to catch schema mismatches early

Common Problem Diagnostics

SymptomLikely causeSolution
Gradient doesn't appear in CSSsync:architecture did not run after themes:generatenpm run tokens:sync + npm run tokens:build:all
New token doesn't appear in dist/Theme not registered in themes.config.jsonAdd entry to the global themes config
Color different from expectedOverride in overrides.* overwriting the generated valueCheck whether an override is configured for that color
Build fails with "reference not found"data/ out of sync with configsnpm run tokens:build (full rebuild)
txtOn is black/white when brand color was expectedtxtOnStrategy: 'high-contrast' is the defaultChange to 'brand-tint' in the theme options
New theme not visible in Tokens Studiofigma:generate not run since adding the themenpm run tokens:figma or npm run tokens:build
Figma style references lost after regenerationdata/$themes.json was deletedRe-sync from Figma; do not delete this file

References