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/