Skip to main content

Theme Configuration

Theme configuration lives in theme-engine/config/. These files define what the engine generates — brand colors, typography, modes, and spacing scales. The engine reads them; your project versions them.


File map

FileControls
global/themes.config.jsonBrand list, modes, surfaces, gradients, dimension scale, typography scale
global/dimension.config.mjsSpatial scale algorithm (spacing, sizing, border radius)
foundations/engine.config.mjsFoundation layer typography and elevation styles
<brand-name>.config.mjsPer-brand primitive colors, typography, and options

All brand names declared in themes.config.json must have a corresponding <name>.config.mjs file.


themes.config.json — global settings

Minimal setup

{
"themes": {
"my-brand": {
"foundation": {
"brand": "engine",
"files": [
"default.json",
"styles/typography_styles.json",
"styles/elevation_styles.json"
]
}
}
},
"global": {
"modes": ["light", "dark"],
"surfaces": ["positive", "negative"],
"gradients": false
}
}

themes — per-brand settings

Each key under themes is a brand ID. The ID must match the name field in the corresponding brand config file and the filename without extension.

FieldTypeDescription
foundation.brandstringFoundation brand identifier (usually "engine")
foundation.filesstring[]Foundation files to include in the build
includePrimitivesbooleanInclude _primitive_theme.json in generated data. Set false for Figma performance.

global — workspace-wide settings

FieldTypeDescription
modes["light"] / ["light", "dark"]Which luminosity modes to generate
surfaces["positive"] / ["positive", "negative"]Which surface contexts to generate
gradientsbooleanGenerate gradient tokens

Typography scale (optional)

{
"global": {
"typography": {
"fontSizeScaleRatio": 1.2,
"fontSizeScale": ["nano", "micro", "extraSmall", "small", "medium", "large", "extraLarge", "mega"]
}
}
}
  • fontSizeScaleRatio — musical scale ratio for font size growth. Common values: 1.125 (Major Second), 1.2 (Minor Third), 1.25 (Major Third), 1.333 (Perfect Fourth).
  • fontSizeScale — ordered list of size keys. The engine maps each key to a scale step.

<brand-name>.config.mjs — per-brand configuration

One file per brand. Defines the primitive values the engine decomposes into the full token set.

Minimal example

export default {
name: 'my-brand',

colors: {
// Brand palette
primary: '#3B82F6',
light: '#EFF6FF',
dark: '#1E3A8A',

// Interface function colors
action: '#2563EB',
action_dark: '#1D4ED8',
link: '#60A5FA',

// Feedback colors
info_blue: '#0EA5E9',
info_blue_dark: '#0284C7',
success_green: '#22C55E',
success_green_dark:'#16A34A',
warning_orange: '#F59E0B',
warning_orange_dark:'#D97706',
danger_red: '#EF4444',
danger_red_dark: '#DC2626',
},

mapping: {
brand: {
first: 'primary',
second: 'light',
third: 'dark'
},
interface: {
function: {
primary: 'action',
secondary: 'action_dark',
link: 'link'
},
feedback: {
info_default: 'info_blue',
info_secondary: 'info_blue_dark',
success_default: 'success_green',
success_secondary: 'success_green_dark',
warning_default: 'warning_orange',
warning_secondary: 'warning_orange_dark',
danger_default: 'danger_red',
danger_secondary: 'danger_red_dark'
}
}
},

typography: {
fontFamilies: {
main: 'Inter',
content: 'Inter',
display: 'DM Sans',
code: 'JetBrains Mono'
}
},

options: {
txtOnStrategy: 'brand-tint',
accessibilityLevel: 'AAA',
acceptAALevelFallback: true,
darkModeChroma: 0.85,
includePrimitives: false,
uiTokens: false,
}
};

colors — primitive color values

Define named hex colors here. These are the raw palette values used by the engine as input to OKLCh decomposition. They are not directly exposed as tokens.

  • Brand colors — the three brand palette anchors (first, second, third)
  • Interface function colors — primary action, secondary action, and link colors
  • Feedback colors — info, success, warning, and danger; each has a default and secondary variant

mapping — semantic role assignment

Maps the named colors to their semantic roles. The engine uses this mapping to generate the full Semantic and Foundation token sets.

KeyRole
brand.firstPrimary brand color — CTAs, key surfaces, brand highlight
brand.secondSecondary brand color — supporting surfaces
brand.thirdTertiary brand color — dark accents, text on brand backgrounds
interface.function.primaryPrimary action color — buttons, active states
interface.function.secondarySecondary action color — secondary buttons
interface.function.linkLink color
interface.feedback.*Info / success / warning / danger, each default + secondary

typography — font families

typography: {
fontFamilies: {
main: 'Inter', // body text, UI labels
content: 'Inter', // long-form content (can differ from main)
display: 'DM Sans', // headings, display text
code: 'JetBrains Mono' // code blocks, monospace
}
}

Font files must be present in assets/fonts/ if you enable copyFonts in the transformers config.

options — generation behavior

OptionDefaultDescription
txtOnStrategy'brand-tint'How text-on-color is calculated. 'brand-tint' uses the closest passing palette color. 'high-contrast' always uses pure black or white.
txtBaseColorLevel140Palette level used as starting point for txt color generation (since 3.6.0). Lower = lighter text.
accessibilityLevel'AAA'WCAG target. 'AAA' = 7:1 ratio, 'AA' = 4.5:1 ratio.
acceptAALevelFallbacktrueWhen true, the engine automatically accepts AA when AAA fails instead of blocking.
darkModeChroma0.85Saturation factor for dark mode palette. 1.0 = same saturation as light. 0.85 = 15% more muted.
includePrimitivesfalseWhether to generate _primitive_theme.json. Set true for Figma use; false reduces Figma file size.
uiTokensfalseGenerate _ui.json token file.

Gradients (optional)

If gradients: true in themes.config.json, add gradient colors to the brand config:

gradientColors: {
first: { lowest: '#bfdbfe', default: '#3B82F6', highest: '#1E3A8A' },
second: { lowest: '#eff6ff', default: '#DBEAFE', highest: '#93c5fd' },
third: { lowest: '#1e40af', default: '#1E3A8A', highest: '#172554' }
}

Workspace config — aplica-theme-engine.config.mjs

Workspace-level generation behavior lives in aplica-theme-engine.config.mjs at the project root, separate from per-brand configs.

generation.colorText (since 3.6.0)

Controls txt token generation — the fourth property in the color contract (background / txtOn / border / txt):

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

export default defineThemeEngineConfig({
generation: {
colorText: {
generateTxt: true,
txtBaseColorLevel: 140,
fallbackBaseColorLevel: 160,
textExposure: ['feedback'],
}
},
paths: { /* your existing paths */ }
});
FieldDefaultDescription
generateTxtfalseEnable txt generation across all color layers
txtBaseColorLevel140Palette level to start looking for an accessible txt color
fallbackBaseColorLevel160Level to fall back to when txtBaseColorLevel does not pass contrast
textExposure['feedback']Which families get foundation.txt.* flat aliases. Add 'interfaceFunction' or 'product' as needed

txtBaseColorLevel can also be overridden per brand in options.txtBaseColorLevel.

See configuration guide and txt token for the full contract.


Naming convention

Brand config filenames and the name field must use the same identifier format as in themes.config.json:

themes.config.json → "my-brand"
config/my-brand.config.mjs → name: 'my-brand'

Underscores and hyphens are both valid but must be consistent across all three references.


After changing configuration

Run the full pipeline to propagate changes:

npm run tokens:build

Or step-by-step if you want to inspect intermediate output:

npm run tokens:themes # reprocess colors
npm run tokens:dimension # if dimension config changed
npm run tokens:sync # propagate references
npm run tokens:foundations # rebuild foundation aliases
npm run tokens:build:all # transform to dist/