npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aplica/aplica-theme-engine

v3.13.7

Published

Configurable core engine for theme generation, design tokens, and multi-platform build output.

Readme

Aplica Theme Engine

@aplica/aplica-theme-engine is the reusable engine package behind Aplica's theme generation and token build pipeline.

It lets a consumer project keep its own theme configuration, generate local data/, and build local dist/ without copying the engine internals into each repository.

The preferred CLI command is theme-engine. The legacy aplica-theme-engine name remains available as a compatibility fallback.

Model Themes

The example themes under config/ are the active Aplica model-theme set.

They are used as:

  • authored examples of valid theme config
  • control fixtures for preview and regression testing
  • reference scenarios for interaction decomposition, accessibility, overrides, gradients, and typography choices

Detailed maintainer-facing guides live in:

What this package does

  • generates brand, mode, surface, semantic, and foundation token data from consumer-owned theme config
  • supports workspace-level color-text generation, including the legacy 3-part general color contract and the expanded 4-part contract with txt
  • generates Tokens Studio / Figma scaffolding files such as data/$themes.json and data/$metadata.json from the active workspace structure
  • builds multi-platform output such as JSON, typed JSON, ESM, CommonJS, CSS, and type declarations
  • exposes a CLI plus stable config helpers for consumer projects
  • keeps a legacy compatibility workspace for core development and transition scenarios

Interaction Decomposition And Surface Presets

In 3.9.0, interaction structure is split between workspace scope and theme scope:

  • aplica-theme-engine.config.mjs
    • owns shared public structure such as legacyStructure and ghost.enabled
  • theme files under config/
    • own per-theme authored values such as decomposition method, direction, and state levels

This split is required for contract safety:

  • shared generated layers such as mode, surface, and semantic cannot expose different public interaction structures per theme in the same workspace
  • because of that, legacyStructure and ghost.enabled must stay global
  • theme files should only declare authored values, not workspace-wide public structure switches

Workspace config example:

generation: {
  interaction: {
    legacyStructure: false,
    ghost: {
      enabled: true
    }
  }
}

Theme config example:

options: {
  interaction: {
    decomposition: {
      method: 'dilution',
      dilution: {
        direction: 'high',
        target: 'canvas',
        fallback: 'ambient-neutral'
      }
    },
    surfaces: {
      solid: {
        levels: {
          action: 0.9,
          active: 0.8,
          focus: 1.2
        }
      },
      ghost: {
        levels: {
          normal: 'transparent',
          action: 0.95,
          active: 0.9,
          focus: 1.1
        }
      }
    }
  }
}

Theme config example with grouped overrides:

options: {
  interaction: {
    decomposition: {
      method: 'system-scale'
    },
    surfaces: {
      solid: {
        levels: {
          action: 120,
          active: 140,
          focus: 50
        }
      },
      ghost: {
        levels: {
          normal: 'transparent',
          action: 40,
          active: 60,
          focus: 20
        }
      }
    },
    groups: {
      function: {
        surfaces: {
          ghost: {
            decomposition: {
              method: 'dilution',
              dilution: { direction: 'low' }
            },
            levels: {
              normal: 'transparent',
              action: 1.0,
              active: 1.1,
              focus: 0.25
            }
          }
        }
      },
      feedback: {
        decomposition: {
          method: 'dilution',
          dilution: { direction: 'low' }
        }
      }
    }
  }
}

What this means:

  • system-scale
    • the legacy interaction behavior, now with an explicit name
    • state values are palette levels such as action: 120
  • dilution
    • state values are per-state dilution factors such as action: 0.9
    • normal is always the authored base color
    • values below 1 move toward the configured direction
    • values above 1 invert the configured direction and use the excess intensity on the opposite extreme
  • direction: 'high'
    • prefers the darker direction according to the engine's scale semantics
  • direction: 'low'
    • prefers the lighter direction according to the engine's scale semantics
  • target: 'fixed'
    • keeps the legacy dilution target behavior
    • low aims at white and high aims at black
  • target: 'canvas'
    • makes dilution quadrant-aware
    • the dilution target becomes the resolved interaction canvas for the active mode and surfaceType
  • target: 'anchor'
    • makes dilution aim at an authored chromatic destination instead of only white/black or the canvas
    • anchor.source can use:
      • palette
      • hex
      • token
    • anchor.canvasAware: true
      • keeps that chromatic anchor quadrant-sensitive
      • light canvases pull it lighter
      • dark canvases pull it darker
  • fallback: 'legacy'
    • if a canvas-aware result collapses too close to the canvas, the engine falls back to the fixed white/black behavior
  • fallback: 'ambient-neutral'
    • if a canvas-aware result collapses too close to the canvas, the engine reuses the nearest visible brand.ambient.neutral tone from the same quadrant
  • legacyStructure: true
    • keeps the current public interaction structure
  • legacyStructure: false
    • enables explicit public solid and ghost groups
  • ghost.normal: 'transparent'
    • means the visible surface is the declared canvas, so txtOn is validated on the effective composited background, not on a raw opaque token
  • groups.function / groups.feedback
    • allow local decomposition and state overrides for one interaction family without forcing the same behavior onto the other
  • groups.<group>.surfaces.<preset>
    • is the most specific interaction override layer
    • merge order is:
      • theme default
      • preset default
      • group default
      • group + preset override
  • baseAdaptation
    • is the systemic opt-in for adapting authored base surfaces by quadrant
    • it is meant for cases where interaction.*.normal or product.*.*.default cannot stay fixed across light/dark and positive/negative
    • it does not replace authored action, active, or focus logic
    • it only adapts:
      • interaction normal
      • product default
  • baseAdaptation.interaction.<group>.<preset>
    • adapts one interaction base branch such as feedback.solid.normal
  • baseAdaptation.product
    • adapts product default surfaces across quadrants without introducing manual overrides

Backward compatibility rules:

  • themes with no options.interaction keep the current behavior
  • legacy options.interfaceFunctionPaletteLevels still works
  • system-scale is the implicit default
  • solid / ghost only become public generated groups when the workspace explicitly sets generation.interaction.legacyStructure: false

Accessibility rules:

  • txtOn is always validated against the effective visible background of the interaction state
  • txt remains the readable family-colored text for the declared ambient canvas
  • the interaction contract is validated in all four quadrants:
    • light-positive
    • light-negative
    • dark-positive
    • dark-negative
  • canvas-aware dilution is now validated as a general capability of method: 'dilution'
    • it is not limited to ghost
    • solid and any grouped override can use the same target / fallback contract
  • quadrant-aware base adaptation is also validated
    • interaction base states and product defaults can now be tested as first-class quadrant-sensitive surfaces

Important Color Contract Modes In 3.6.x

In the active 3.6.x line, the Theme Engine treats general readable-color generation as a workspace-level decision configured in aplica-theme-engine.config.mjs.

The active controls live under:

  • generation.colorText.generateTxt
  • generation.colorText.txtBaseColorLevel
  • generation.colorText.fallbackBaseColorLevel
  • generation.colorText.textExposure

Example:

import { defineThemeEngineConfig } from '@aplica/aplica-theme-engine/config';

export default defineThemeEngineConfig({
  generation: {
    colorText: {
      generateTxt: false,
      txtBaseColorLevel: 140,
      fallbackBaseColorLevel: 130,
      textExposure: {
        feedback: true,
        interfaceFunction: false,
        product: false
      }
    }
  }
});

What each key means:

  • generateTxt
    • false keeps the smaller legacy general color contract
    • true enables the expanded contract with txt
  • txtBaseColorLevel
    • preferred starting level when the engine is generating txt
  • fallbackBaseColorLevel
    • preferred starting level when generateTxt is off and the engine still needs to derive a readable fallback text candidate
  • textExposure
    • controls which readable text families rise into mode, surface, semantic, and simplified foundation.txt
    • default is feedback only
    • interfaceFunction and product are opt-in

Default guidance for the current release line:

  • older or migrated workspaces should stay on generateTxt: false unless you are intentionally moving them to the expanded readable-text contract
  • new starter/model scaffolds now also default to includePrimitives: false so the first generated workspace stays lighter and easier to validate

Which Config Owns What

For tutorials and integrations, keep this split explicit:

  • aplica-theme-engine.config.mjs
    • owns workspace-wide generation behavior
    • owns generation.colorText.generateTxt
    • owns generation.colorText.txtBaseColorLevel
    • owns generation.colorText.fallbackBaseColorLevel
    • owns generation.colorText.textExposure
    • owns generation.interaction.legacyStructure
    • owns generation.interaction.ghost.enabled
  • theme-engine/config/*.config.mjs
    • own theme-authored input
    • colors
    • mapping
    • typography
    • per-theme overrides
    • txtOnStrategy and other theme-local options
    • options.interaction.decomposition.*
    • options.interaction.surfaces.solid.levels
    • options.interaction.surfaces.ghost.levels

This distinction is important:

  • the theme file describes the theme itself
  • the workspace config describes how the engine should generate the workspace contract
  • mixed public interaction structures inside one workspace would break shared mode / surface / semantic contracts, so the engine treats those switches as workspace-scoped on purpose

Use these modes:

  • generateTxt: false:
    • keeps the legacy 3-part general color contract
    • background
    • txtOn
    • border
  • generateTxt: true:
    • activates the expanded 4-part contract
    • background
    • txtOn
    • border
    • txt

When generateTxt: true, the systemic contract is:

  • background
  • txtOn
  • border
  • txt

This remains an important paradigm shift, not just an extra field:

  • txtOn still means text or icon color used directly on the colored surface
  • txt is now a separate generated text color that keeps the same semantic family while remaining readable on the declared theme.color.{mode}.brand.ambient.contrast.base.positive.background canvas of the active file
  • txt is accessibility-normalized, just like the rest of the generated color system
  • mode, surface, semantic, brand, grayscale, and primitive outputs participate in the updated quartet whenever the workspace enables generateTxt

Important Text Contract Shift In 3.6.x

The 3.6.x line also changes how the Theme Engine generates and exposes text tokens.

  • txtOn keeps the same meaning and is not being redefined by this work
  • first-level readable aliases such as info, warning, primary, or promo now resolve from text sources, not from surface.*
  • those simplified aliases represent the corresponding default readable value of that family
  • structured text states are now exposed upward through brand, mode, surface, and semantic
  • generated foundations expose only the simplified first-level readable aliases in foundation.txt

That means the engine now exposes text behavior intentionally:

  • feedback text can expose normal, action, active, and focus
  • interface function text can expose normal, action, active, and focus
  • product text can expose its intensity ladder through txt
  • generated foundations now flatten the readable default values into first-level aliases such as foundation.txt.info, foundation.txt.primary, and foundation.txt.promo
  • by default only feedback readable text is promoted upward; interfaceFunction and product remain opt-in through generation.colorText.textExposure

Use this rule of thumb:

  • use txtOn when text sits directly on the colored surface
  • use txt or the structured text states when you need that semantic family on an established ambient or contrast background

If you need the new txt token to start from a different preferred level than border, use the workspace config:

  • generation.colorText.txtBaseColorLevel with default readable-text search starting at 140
  • generation.colorText.fallbackBaseColorLevel with fallback readable search starting at 130 when generateTxt is off and the engine must choose a readable text candidate without emitting txt

Those controls let the workspace steer the preferred source levels, while the engine still enforces accessibility on the final generated value.

The package's authored model for this release line is theme_samples/aplica-themes, which now demonstrates:

  • the generation.colorText workspace contract
  • the optional background / txtOn / border / txt quartet
  • the readable-text shift from surface.* to txt.*
  • the simplified foundation.txt.* aliases
  • optional readable-text promotion through generation.colorText.textExposure
  • the split between workspace-level generation config and theme-level authored config

Breaking Upgrade Notes For 3.6.x

Treat the full 3.6.x line as a breaking upgrade line.

Even though the package version is in the 3.6.x range, this release changes the generated contract deeply enough that existing consumers should assume migration work is required.

If you already use @aplica/aplica-theme-engine, review these points before adopting 3.9.0:

  • 3.9.0 adds authored interaction decomposition and expanded surface presets: system-scale, dilution, optional public solid / ghost, and state-level authored control through options.interaction
  • 3.9.0 preserves the legacy public interaction structure by default; solid / ghost only appear when the workspace explicitly sets generation.interaction.legacyStructure = false
  • 3.9.0 keeps options.interfaceFunctionPaletteLevels working as a legacy-compatible input and normalizes it into the new interaction contract internally
  • 3.9.0 adds theme-engine preview, which generates a static visual HTML preview for semantic colors, txtOn, txt, borders, typography, and elevation from the current workspace dist/
  • 3.8.4 finishes the negative readable-text fix in the final distributable outputs: interface.function.*.txt, interface.feedback.*.txt, interface.function.disabled.*.txt, and negative product.*.txt now resolve against the correct mirrored canvas, and the package now audits all emitted dist/json/* *.txt leaves against color.brand.ambient.contrast.base.positive.background
  • 3.8.4 also adds mode.productBySurface to preserve product polarity through mode -> surface -> semantic, including freshly bootstrapped workspaces that create data/mode/*.json from scratch
  • 3.8.3 fixes the remaining ambient surface-polarity gap: surface negative now resolves the whole brand.ambient family from the inverse mode canvas, and ambient.contrast.*.negative.txt / ambient.neutral.*.txt / ambient.grayscale.*.txt are all generated against the declared contrast.base.positive.background canvas of the final file
  • 3.8.2 fixes brand.branding polarity in surface / semantic: negative now resolves branding txtOn, border, and txt from the inverse mode branch instead of flattening to the same output as positive
  • 3.8.2 does not force authored branding backgrounds to differ by surface; if the source theme uses the same theme.color.light|dark.brand.branding.*.background, the generated background may still match across positive/negative
  • 3.8.1 corrects the accessibility target for ambient txt: txtOn still validates on the level's own background, while ambient.*.txt now follows the declared contrast canvas contract instead of the local level background
  • 3.8.0 introduces theme-engine as the preferred CLI alias while keeping aplica-theme-engine available as a compatibility fallback
  • the generated general color shape is now controlled globally in aplica-theme-engine.config.mjs through generation.colorText.generateTxt
  • when generateTxt is enabled, the generated general color shape is no longer background / txtOn / border; it is background / txtOn / border / txt
  • 3.7.1 fixes the ambient txt inversion regression in the expanded quartet path, so positive and negative ambient readable-text branches are resolved against the correct background context instead of collapsing to one mode-wide candidate
  • 3.7.2 fixes brand.branding propagation so mode-aware border and txt values now survive the trip from theme.color.* through mode, surface, semantic, and final dist/ outputs instead of flattening too early in surface.brand.branding
  • 3.7.4 fixes structured readable-text inversion for feedback and interfaceFunction, validates those generated readable branches against their real positive/negative ambient backgrounds, and stops generating public product.*.txt tokens when generation.colorText.textExposure.product is off
  • 3.7.5 removes invalid txt: null placeholders from disabled product readable-text branches, which fixes Tokens Studio / Figma loading errors in consumer workspaces that enabled generateTxt globally but intentionally kept product readable text off
  • readable text aliases are no longer allowed to come from surface.*; they now come from txt.*
  • flat readable text aliases now mean the family's default readable text value, equivalent to txt.normal
  • foundation.txt no longer behaves like the older nested readable-text surface; it is now simplified to first-level aliases such as foundation.txt.info, foundation.txt.primary, and foundation.txt.promo
  • only feedback readable text is promoted by default; interfaceFunction and product will not appear unless the workspace explicitly enables them through generation.colorText.textExposure
  • when generation.colorText.textExposure.product is false, public product branches now stay on the smaller background / txtOn / border contract in mode, surface, and semantic
  • existing consumers should expect diffs not only in data/semantic/default.json, but also in data/brand/*, data/mode/*, data/surface/*, data/foundation/*, and the derived dist/ output
  • converted legacy consumers now preserve the source workspace generation.colorText contract automatically during migration, so parity and rebuild behavior no longer silently fall back to the no-txt defaults when the source fixture already publishes the expanded quartet

Recommended upgrade checklist for existing consumers:

  1. Rebuild the workspace with theme-engine build.
  2. Run theme-engine validate:data.
  3. Review the generated diffs in data/ and dist/ instead of assuming path compatibility.
  4. Move any old text-generation controls out of theme-level options.* and into aplica-theme-engine.config.mjs under generation.colorText.*.
  5. Audit any consumer code, token references, Figma mappings, or foundation aliases that depended on the old 3-part color contract or on old readable-text paths.
  6. If you need readable text for interfaceFunction or product, enable them intentionally through generation.colorText.textExposure.
  7. Use config/aplica-blue-sky.config.mjs and theme_samples/aplica-themes as the reference model for the new authored contract.

Fallbacks and non-changes to keep in mind:

  • txtOn is unchanged; this work does not redefine text-on-surface behavior
  • txt is different from txtOn: it is the readable family-colored text meant for the declared ambient contrast background, not for the token's own local block background
  • when generateTxt: true, the new txt search starts from level 140, then walks to darker levels until one passes accessibility; if none passes, the engine falls back to black
  • when generateTxt: false, readable fallback text starts from fallbackBaseColorLevel (130 by default), continues through darker levels until one passes, and falls back to black if the scale cannot satisfy accessibility
  • when generation.colorText.textExposure is omitted, the default remains feedback: true, interfaceFunction: false, product: false
  • if you do not opt into extra readable-text families, the generated contract stays intentionally smaller at the foundation.txt layer

Legacy and migration expectations in the current package line:

  • the internal compatibility fixture and the validated legacy migration path now both keep generateTxt: false by default
  • the package smoke test, compatibility suite, and legacy parity suite all validate that smaller contract path
  • the migrator now writes a portable theme-engine/lib/schema-loader.mjs into converted consumers when they need local schema runtime support
  • legacy duplicated dist/css/semantic/* files are still reported during parity analysis, but they are treated as expected report-only drift instead of blocking failures

Preferred model

The recommended setup is:

consumer-project/
  aplica-theme-engine.config.mjs
  theme-engine/
    config/
      global/
      foundations/
      my-brand.config.mjs
    schemas/
    transformers.config.mjs
  data/
  dist/

The consumer owns the configuration and generated artifacts. The package owns the engine logic, schemas, defaults, and CLI.

Install

npm install @aplica/aplica-theme-engine

For CLI usage, prefer theme-engine. The previous aplica-theme-engine command still works as a fallback while downstream projects migrate.

To bootstrap a new workspace without installing the package globally, use:

npx @aplica/aplica-theme-engine@latest init

Then add consumer scripts like:

{
  "scripts": {
    "tokens:build": "theme-engine build",
    "tokens:build:all": "theme-engine build:all",
    "tokens:themes": "theme-engine themes:generate",
    "tokens:dimension": "theme-engine dimension:generate",
    "tokens:sync": "theme-engine sync:architecture",
    "tokens:foundations": "theme-engine foundations:generate",
    "tokens:figma": "theme-engine figma:generate",
    "tokens:validate": "theme-engine validate:data",
    "tokens:preview": "theme-engine preview",
    "tokens:ai": "theme-engine ai:init",
    "tokens:ai:guidance": "theme-engine ai:guidance"
  }
}

Recommended meaning:

  • tokens:build: preferred full pipeline for consumer projects
  • tokens:build:all: rebuild dist/ from the current data/ only
  • tokens:themes: regenerate brand theme files
  • tokens:dimension: regenerate dimension outputs
  • tokens:sync: propagate structure into mode, surface, semantic, and foundation
  • tokens:foundations: regenerate foundation aliases and styles
  • tokens:figma: regenerate Tokens Studio / Figma scaffolding files
  • tokens:validate: validate generated data/ against the active schemas
  • tokens:preview: generate a static visual preview in dist/preview/
  • tokens:ai: inject the distributed AI guidance templates into the current workspace
  • tokens:ai:guidance: compose a portable Markdown AI guidance bundle into dist/

AI Guidance Distribution

To ship consumer-owned AI usage guidance alongside compiled token artifacts, add an ai.guidance block to aplica-theme-engine.config.mjs and run theme-engine ai:guidance.

import { defineThemeEngineConfig } from '@aplica/aplica-theme-engine/config';

export default defineThemeEngineConfig({
  ai: {
    guidance: {
      enabled: true,
      output: './dist/AI_GUIDANCE.md',
      header: {
        title: 'AI Guidance',
        intro: 'Prefer compiled outputs in `dist/` over guessed token names.',
        metadata: {
          audience: 'AI coding tools',
          contract: 'portable dist bundle'
        }
      },
      sources: [
        './docs/context/ai-ui/UI_TOKEN_CONSUMPTION_CONTRACT.md',
        './docs/context/tokens/token-usage-for-components-and-figma.md',
        {
          path: './docs/context/ai-ui/TYPOGRAPHY_AND_ELEVATION_STYLE_USAGE.md',
          order: 30
        }
      ]
    }
  }
});

Notes:

  • the feature is opt-in and does nothing unless ai.guidance.enabled is true
  • source Markdown files stay consumer-owned and can live anywhere inside the workspace
  • output defaults to ./dist/AI_GUIDANCE.md
  • source order follows the array order unless you provide an explicit numeric order
  • generated output is deterministic and safe for CI use

Typed JSON Output

The active package line also supports an additive typed JSON output for downstream engineering targets that need token metadata preserved in the generated artifact.

This is exposed as the built-in jsonTyped platform in theme-engine/transformers.config.mjs.

Example:

import { defineTransformersConfig } from '@aplica/aplica-theme-engine/transformers/config';

export default defineTransformersConfig({
  layers: {
    semantic: {
      enabled: true,
      platforms: ['json', 'jsonTyped', 'esm', 'js', 'css']
    }
  },
  output: {
    directories: {
      json: './dist/json',
      jsonTyped: './dist/json-typed',
      esm: './dist/esm',
      js: './dist/js',
      css: './dist/css/semantic'
    }
  },
  formatOptions: {
    jsonTyped: {
      typeKey: 'type',
      valueKey: 'value',
      descriptionKey: 'description',
      includeDescription: true
    }
  }
});

What jsonTyped does:

  • preserves token metadata in the generated payload
  • emits nested token objects with type and value
  • can optionally include description and path
  • is additive, so it does not change the existing json contract

Important behavior:

  • jsonTyped preserves the current token semantics instead of forcing a single scalar type for every token
  • that means value is still token-dependent
  • for example:
    • colors remain strings such as #265ed9
    • spacing and radii remain dimension strings such as 12px
    • many fontWeights are numeric, but some can still be emitted as strings when the active token contract uses a named value

So the recommended consumer assumption is:

  • type is the stable discriminator
  • value should be interpreted according to that token type

Current boundary:

  • consumers can enable the built-in jsonTyped platform per layer
  • consumers can customize the output directory and metadata key names for jsonTyped
  • consumers still cannot register an entirely new output platform or arbitrary renderer through config alone

Recommended use cases:

  • Dart / Flutter token ingestion
  • typed mobile pipelines
  • engineering tooling that needs token type metadata instead of plain resolved values

Reference:

Quick start

Start with:

theme-engine init

That command opens two onboarding paths:

  • Load starter template: scaffolds a ready-to-run consumer workspace with one example theme, default package schemas, and starter defaults tuned for lower memory usage
  • Create using the wizard: scaffolds the consumer workspace and also generates theme-engine/schemas/architecture.mjs from guided schema answers

Starter-template specifics in the current package line:

  • the starter theme is scaffolded with includePrimitives: false
  • the command prints an English welcome banner with:
    • package version
    • package license
    • key docs entry points
    • an optional support / coffee link

This keeps the starter workspace lighter by default while still leaving includePrimitives available as an authored theme option when a consumer really needs primitive output.

After init, run:

theme-engine build

If you want the package to scaffold a consumer-owned architecture.mjs schema for you, run:

theme-engine schemas:helper

That helper asks structural questions and writes a starter schema, typically in theme-engine/schemas/architecture.mjs.

If you also want the package to inject the distributed AI guidance surfaces for the current workspace, run:

theme-engine ai:init

That command injects the current distributed guidance templates into the consumer workspace, including:

  • docs/context/aplica-ui-integration.md
  • .cursor/rules/
  • .claude/skills/
  • .github/instructions/

theme-engine build is the preferred consumer command. It runs the full pipeline:

  1. ensure:data
  2. themes:generate
  3. dimension:generate
  4. sync:architecture
  5. foundations:generate
  6. figma:generate
  7. build:all

If you want to validate the generated data/ contract before or after a build, run:

theme-engine validate:data

That command validates the generated token layers in data/ against the active workspace schemas and current generation contracts.

If you want a quick visual check of the generated colors and helper styles, run:

theme-engine preview

That command writes a static preview bundle to:

  • dist/preview/index.html
  • dist/preview/preview-data.js
  • dist/preview/preview-app.js
  • dist/preview/preview-app.css

The preview reads the current workspace dist/ output and the active workspace schema snapshot, and lets you inspect:

  • semantic brand, interface, product, and text tokens
  • background, border, txtOn, and txt
  • typography helper classes
  • elevation helper classes
  • light / dark
  • positive / negative

In the active 3.9.0 line, the preview is schema-aware:

  • semantic ordering and grouping follow the active architecture schema and workspace interaction contract
  • public solid / ghost rendering follows the current workspace structure instead of assuming only the legacy grouping
  • foundation preview sections are resolved from generated foundation output instead of relying only on a fixed hardcoded subset
  • the UI now includes a View toggle:
    • Detailed keeps the full section-by-section explorer
    • Summary renders a condensed quartet table focused on background, txtOn, border, txt, and contrast so QA can compare color behavior faster across quadrants

If you want to rebuild first and then generate the preview in one step, run:

theme-engine preview --build

If you want the preview to stay available through a local static server so the HTML, CSS, JS, fonts, and helper styles behave the same way they do in a browser, run:

theme-engine preview --serve

In the current line, preview --serve also watches the active workspace dist/ output. When semantic JSON, helper CSS, foundations, or other preview-relevant dist artifacts change, the preview bundle is regenerated and the browser reloads automatically.

You can combine both when you want a fresh build and an immediately browsable preview:

theme-engine preview --build --serve

For the full setup, migration notes, and pre-publish validation flow, see Consumer Package Guide and Publish Checklist.

Core validation scripts

Inside this repository, the main maintainer checks are:

  • npm run test:compat
    • validates legacy workspace mode, consumer fixture mode, config loading, and Figma scaffolding behavior
  • npm run test:package-smoke
    • packs the current package, installs it into a temporary consumer workspace, and validates a real build flow
  • npm run test:legacy-parity
    • reruns the legacy migration parity suite against the validated internal references

These are maintainer-level validation scripts for the package repository itself, not scripts that downstream consumers need to copy verbatim.

Where to read next

Use the documentation in layers:

Recommended reading paths:

  • new consumer onboarding: README#00#01#02#03Consumer Package Guide
  • system understanding: #04#06#07#09#10
  • theme authoring and discovery: #02#03#11
  • systemic color model changes: READMEdocs/context/THEME_CONFIG_REFERENCE.mddocs/context/DYNAMIC_THEMES.mddocs/context/dynamic-themes-reference/COLOR-DECOMPOSITION-SPEC.md#10

Historical material is preserved for traceability only:

  • docs/context/legacy/: internal historical context and transition notes
  • docs/legacy/: archived user-facing docs that no longer describe the current package model

AI UI reference program

The repository also maintains an internal AI-guided UI library integration program for teaching and validating how LLMs should apply Aplica tokens in real component implementations.

Start with:

Current validated and in-validation reference set:

  • Button
  • Dialog
  • Input
  • Badge
  • Select
  • Card
  • Tabs

This program is the maintainer-facing source of truth for:

  • AI token-consumption rules
  • sanctioned typography and elevation style usage
  • component archetype guidance
  • distributed skill/template behavior for other AI tools

Important boundary:

  • this program helps us train and distribute better AI guidance
  • the sandbox used to validate that guidance is internal to this repository
  • it is not part of the published npm package contract
  • downstream consumers are expected to use the package outputs and the distributed AI guidance, not this repo's sandbox implementation directly
  • distributed skill surfaces should stay objective and point to the contract/archetypes instead of copying internal validation details inline

Legacy migration parity

The package also includes a legacy migrator focused on one critical guarantee:

  • reproduced data/ outputs must match the legacy reference
  • reproduced dist/ outputs must match the legacy reference

This is the migration success rule. Metadata drift, validation artifacts, and wrapper/index drift may still appear in reports, but they do not fail migration parity by themselves.

Recommended real-project flow:

theme-engine migrate:legacy-consumer run --source <legacy-project>

That command:

  • analyzes the legacy project
  • selects the migration profile you requested, or the recommended one when omitted
  • converts the workspace
  • runs the correct build command for the chosen profile
  • compares converted data/ and dist/ against the legacy reference

You can still run each stage separately:

theme-engine migrate:legacy-consumer analyze --source <legacy-project>
theme-engine migrate:legacy-consumer convert --source <legacy-project>
theme-engine migrate:legacy-consumer compare --source <legacy-project>

For repeated test runs against the same target workspace, use --force with convert or run to replace the previous converted workspace.

The migrator currently supports two conversion profiles:

  • config-driven: for legacy repos that still have real config/ and schemas/
  • output-seeded: for legacy repos where data/ and dist/ are the reliable source of truth

If you omit --profile, the migrator analyzes the source project and uses the recommended profile for that layout.

Each converted consumer gets its own schema surface under theme-engine/schemas/:

  • copied legacy schemas when they are portable
  • inferred architecture.mjs when the legacy project has no schema source
  • package-backed bridge schemas for runtime helpers that must remain available in the converted workspace

Validated internal migration references cover both config-driven and output-seeded legacy profiles, and they currently achieve output parity.

Reports and converted workspaces are written under temp/outputs/legacy-migration/, leaving the source fixtures untouched.

Maintainers can rerun the full regression suite with:

npm run test:legacy-parity

Public consumer APIs

  • @aplica/aplica-theme-engine/config
  • @aplica/aplica-theme-engine/transformers/config
  • @aplica/aplica-theme-engine/helpers/dimension-scale
  • @aplica/aplica-theme-engine/schemas/architecture
  • @aplica/aplica-theme-engine/schemas/generation/architecture
  • @aplica/aplica-theme-engine/schemas/semantic-token-descriptions
  • @aplica/aplica-theme-engine/schemas/descriptions/semantic-token-descriptions
  • @aplica/aplica-theme-engine/schemas/typography-scale
  • @aplica/aplica-theme-engine/schemas/generation/typography-scale
  • @aplica/aplica-theme-engine/schemas/typography-styles
  • @aplica/aplica-theme-engine/schemas/generation/typography-styles
  • @aplica/aplica-theme-engine/schemas/foundation-styles
  • @aplica/aplica-theme-engine/schemas/generation/foundation-styles
  • @aplica/aplica-theme-engine/schemas/foundation-style-defaults
  • @aplica/aplica-theme-engine/schemas/defaults/foundation-style-defaults
  • @aplica/aplica-theme-engine/schemas/foundation-token-descriptions
  • @aplica/aplica-theme-engine/schemas/descriptions/foundation-token-descriptions
  • @aplica/aplica-theme-engine/schemas/data

CLI commands

  • theme-engine build
  • theme-engine build:themes
  • theme-engine build:all
  • theme-engine build:semantic
  • theme-engine build:foundation
  • theme-engine build:components
  • theme-engine init
  • theme-engine consumer:init
  • theme-engine ai:init
  • theme-engine ai:guidance
  • theme-engine ai:bundle
  • theme-engine ai:dist
  • theme-engine ai:setup
  • theme-engine skills
  • theme-engine skills:init
  • theme-engine themes:generate
  • theme-engine themes:single
  • theme-engine themes:figma
  • theme-engine ensure:data
  • theme-engine dimension:generate
  • theme-engine sync:architecture
  • theme-engine sync:architecture:test
  • theme-engine sync:architecture:schema
  • theme-engine schemas:helper
  • theme-engine schemas:init
  • theme-engine validate:data
  • theme-engine preview
  • theme-engine data:validate
  • theme-engine foundations:generate
  • theme-engine figma:generate
  • theme-engine migrate:legacy-consumer
  • theme-engine legacy:migrate

Compatibility note:

  • theme-engine ai:init is the recommended AI guidance injection command
  • ai:setup, skills, and skills:init are supported aliases
  • theme-engine ai:guidance generates a consumer-owned Markdown bundle in dist/
  • ai:bundle and ai:dist are supported aliases

Expected runtime behavior

  • theme-engine build is a full generation + build pipeline for consumer projects.
  • theme-engine figma:generate regenerates the Tokens Studio / Figma scaffolding files in data/ from the active theme/foundation structure.
  • theme-engine preview generates a static visual artifact in dist/preview/ from the current dist/ output. Use theme-engine preview --build when you want the command to rebuild before rendering the preview.
  • theme-engine preview --serve starts a small local static server for the generated preview so scripts, fonts, and helper styles run in a browser context. Combine it with --build when you want a fresh preview before serving.
  • inside the preview UI, View > Summary provides a condensed color-comparison mode for quartet tokens (background, txtOn, border, txt) while preserving the existing detailed explorer view.
  • data/$themes.json is merged structurally and preserves Figma-owned IDs and references when entries already exist.
  • theme-engine build:all only transforms the current data/ into dist/. Use it when data/ is already prepared.
  • If the workspace has no consumer config yet, build commands fail fast with a clear message asking you to run theme-engine init.
  • Semantic artifact names are derived from the active layers.semantic.outputPattern in the consumer transformers config. Known file extensions in the pattern are normalized away before the build appends the platform extension.
  • Foundation artifacts keep a stable namespace contract per foundation brand:
    • dist/json/foundation/<foundationBrand>/foundation.json
    • dist/js/foundation/<foundationBrand>/foundation.cjs
    • dist/esm/foundation/<foundationBrand>/foundation.mjs
  • Font assets are consumer-owned. If assets/fonts does not exist in the consumer workspace, the build warns and skips font copy/manifest generation instead of failing.
  • If data/components does not exist, build:components is skipped with an informational message instead of failing.
  • Theme generation may emit accessibility warnings when a palette does not satisfy AAA contrast. When the theme/config allows AA fallback, the engine logs the fallback and continues. Treat those messages as a theme-quality review signal, not as a package crash.
  • If gradients are enabled in config but themes:generate has not populated brand gradients yet, sync:architecture warns that gradients were not propagated. Run the full pipeline or generate themes before syncing architecture.

Consumer vs core scripts

  • Consumer projects should prefer the public CLI commands such as theme-engine build.
  • The package.json scripts inside this repository exist mainly for core development, fixture validation, and release checks.

Safety model

  • Consumer-configured workspace paths are validated to stay inside the active workspace root.
  • Consumer .mjs config files are treated as trusted executable code from the consumer project.
  • The core package does not require consumers to edit engine internals to define their themes.

Schema overrides

  • Package schemas are the default source of truth.
  • A consumer can override them by setting paths.schemasDir in aplica-theme-engine.config.mjs, typically to ./theme-engine/schemas.
  • The engine resolves schemas from the consumer workspace first and falls back to the package defaults.
  • Compatibility tests now cover this override path, so custom consumer schemas are part of the supported contract.
  • Generated output schemas now live in schemas/data/ and can be validated through theme-engine validate:data.
  • The legacy migrator also preserves this rule: every converted workspace must expose its own schema surface, either copied from the legacy consumer or inferred from legacy outputs.

Repository modes

This repository still supports two modes:

  • legacy compatibility workspace at the repository root
  • preferred consumer workspace through the package model

New integrations should follow the consumer workspace model first.

More docs

License

License

Copyright 2018-2026 Poe Bellentani

Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.

Trademarks: “Aplica” and “Aplica Design” are trademarks of Poe Bellentani; registration is pending at INPI (Brazil). The Apache License does not grant rights to use these names as product or service branding beyond customary attribution.


Support

Ko-fi

If this project helped you, consider supporting it on Ko-fi. Donations sustain the open-source tooling behind Aplica Design and the YouTube channel — they do not imply commercial support or an additional license.

https://ko-fi.com/aplicadesign