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/:
- Reads
colorsandmappingfrom the config - For each declared color, calls
ColorDecomposerwhich:- 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) andborderfor each level - Inverts the scale for dark mode (level N → level 200-N)
- Generates
_typography.jsonviaTypographyGenerator - Generates
_borders.jsonwith references to the dimensional scale
Files produced per theme in data/brand/<theme>/:
| File | Contents |
|---|---|
_primitive_theme.json | Raw decomposed palettes (palette + neutrals + behavior per color) |
_grayscale.json | Fixed grayscale scale (15 levels, customizable via override) |
_brand.json | Semantic mapping: theme.color.light.* and theme.color.dark.* |
_typography.json | Typography tokens (families, weights, sizes, line-heights) |
_borders.json | Border radius with references to the dimensional scale |
_ui.json | Component 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):
| File | What is propagated |
|---|---|
data/mode/light.json | Refs from theme.color.light.* for feedback, function, ambient |
data/mode/dark.json | Refs from theme.color.dark.* with scale inversion |
data/surface/positive.json | Refs from mode for positive context |
data/surface/negative.json | Refs from mode with inverted scale (photographic negative) |
data/semantic/default.json | Complete semantic tokens referencing surface |
data/foundation/engine/default.json | Foundation 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:
| File | Purpose |
|---|---|
data/$themes.json | Active theme entries imported by Tokens Studio. Preserves Figma-owned fields (id, $figmaStyleReferences, variable IDs) on merge. |
data/$themes.engine.json.template | Engine-owned canonical template — same structure as $themes.json with empty Figma fields. Used as a reset reference. |
data/$metadata.json | Token-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:
| Platform | Main transformation | Output unit |
|---|---|---|
json | Resolves references, maintains nested structure | px |
css | Generates --semantic-* and --foundation-*, converts dimensions | rem |
esm | Generates ES module with exported object | px |
js | Generates CommonJS module | px |
dts | Generates 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:
ensure:data— creates necessary directories indata/dimension:generate— generates dimensional scalethemes:generate— decomposes colors for all themessync:architecture— propagates references between layersfoundations:generate— generates foundation stylesfigma:generate— generates Tokens Studio / Figma scaffolding filesbuild:all— Style Dictionary →dist/
Incremental builds — use for targeted changes
| Change | Required commands |
|---|---|
| Change a theme's color | tokens:themes → tokens:build:all |
| Change dimensional scale | tokens:dimension → tokens:build:all |
| Change schema (add feedback/product) | tokens:sync → tokens:themes → tokens:build:all |
| Change foundation | tokens:foundations → tokens:build:all |
| Add / rename theme, surface, or mode | tokens: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:
| Section | What it shows |
|---|---|
| Colors | background, border, txtOn, txt across every semantic family, state, and intensity level |
| Typography | All foundation typography classes applied to live sample text |
| Elevation | All 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
txtOncontrast andtxtcanvas colors are correct before syncing to Figma. - When validating ghost surfaces —
ghost.normal.backgroundis transparent, so checking the effective composited canvas requires a visual tool.
Add to the incremental build table:
| Change | Required commands |
|---|---|
| Inspect output visually | theme-engine preview (or theme-engine preview --build to rebuild first) |
The preview renders against the current
dist/output. Run with--buildto ensuredist/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. ConfigureaccessibilityLevelandacceptAALevelFallbackin the themeoptionsto control behavior. - Schema structure:
sync:architecture:testvalidates whether theme configs align with the schema without writing anything - Data integrity: Run
theme-engine validate:databefore publishing to catch schema mismatches early
Common Problem Diagnostics
| Symptom | Likely cause | Solution |
|---|---|---|
| Gradient doesn't appear in CSS | sync:architecture did not run after themes:generate | npm run tokens:sync + npm run tokens:build:all |
| New token doesn't appear in dist/ | Theme not registered in themes.config.json | Add entry to the global themes config |
| Color different from expected | Override in overrides.* overwriting the generated value | Check whether an override is configured for that color |
| Build fails with "reference not found" | data/ out of sync with configs | npm run tokens:build (full rebuild) |
| txtOn is black/white when brand color was expected | txtOnStrategy: 'high-contrast' is the default | Change to 'brand-tint' in the theme options |
| New theme not visible in Tokens Studio | figma:generate not run since adding the theme | npm run tokens:figma or npm run tokens:build |
| Figma style references lost after regeneration | data/$themes.json was deleted | Re-sync from Figma; do not delete this file |
References
- Configuration guide: 03-configuration-guide.md
- Output formats in detail: 05-output-formats.md
- CLI reference: 09-engineering/05-cli-reference.md
- Build and CI integration: 09-engineering/06-build-and-ci.md
- Troubleshooting: 09-engineering/07-troubleshooting.md
- Mathematics and algorithms: 06-mathematics-and-algorithms.md