@absoluteui/theme/designer
Theme Designer engine: theme families, seed-to-role derivation, compliance assessment, and exports.
import { ... } from '@absoluteui/theme/designer';How to use
Section titled “How to use”The Theme Designer engine builds a theme family (light, dark, and high-contrast modes) from a few brand colors, assesses it against WCAG 2.2 AA, and exports it for the theme runtime. It is pure functions with no DOM access: use it in build scripts, internal tools, or a settings screen that lets tenants brand the application. Applications that only apply themes never need it. The Theming guide explains families and export formats.
How do I derive a theme from brand seed colors?
Section titled “How do I derive a theme from brand seed colors?”Pass the seeds you have; the rest come from the defaults. Every text, border, and indicator color is solved to meet its contrast pairing in each mode, so a seed may come back adjusted (here the dark mode lightens the primary color).
import { deriveFamily } from '@absoluteui/theme/designer';
const family = deriveFamily({ name: 'acme-hr', seeds: { primary: '#0F766E', accent: '#7C3AED' }, version: '1.2.0', author: 'Acme HR Platform Team',});family.modes.light.tokens['color-primary']; // => '#0F766E'family.modes.dark.tokens['color-primary']; // => '#5FB6AD'family.modes.light.tokens['color-on-primary']; // => '#FFFFFF'How do I check it against WCAG?
Section titled “How do I check it against WCAG?”assessCompliance checks every mode for text and non-text contrast, focus visibility, state differentiation, target size, zoom, motion, and high contrast. compliant is true only when no required rule fails; each finding names the tokens, components, and a fix. Re-assess after every manual override.
import { assessCompliance, deriveFamily } from '@absoluteui/theme/designer';
const family = deriveFamily({ name: 'acme-hr', seeds: { primary: '#0F766E' } });assessCompliance(family).compliant; // => true
const light = family.modes.light;const edited = { ...family, modes: { ...family.modes, light: { ...light, tokens: { ...light.tokens, 'color-text-muted': '#B0B0B0' } } } };const report = assessCompliance(edited);report.compliant; // => falseconst worst = report.findings[0];[worst?.rule, worst?.measured, worst?.expected]; // => ['WCAG 2.2 1.4.3', '2.16:1', '4.5:1']worst?.remediation; // => 'Change color-text-muted to #767676 or darker/lighter to reach 4.5:1.'How do I apply a family at runtime without exporting it?
Section titled “How do I apply a family at runtime without exporting it?”familyToThemes flattens the family into one theme per mode (<name>, <name>-dark, <name>-high-contrast) that registerTheme accepts. Densities, responsive values, and <name>-system need the exported theme.css.
import { configureAbsoluteUI } from '@absoluteui/core';import { registerTheme, startThemeRuntime } from '@absoluteui/theme';import { deriveFamily, familyToThemes } from '@absoluteui/theme/designer';
const family = deriveFamily({ name: 'acme-hr', seeds: { primary: '#0F766E' } });familyToThemes(family).map((theme) => registerTheme(theme).name); // => ['acme-hr', 'acme-hr-dark', 'acme-hr-high-contrast']startThemeRuntime();configureAbsoluteUI({ common: { theme: 'acme-hr-dark' } });getComputedStyle(document.documentElement).getPropertyValue('--aui-color-primary'); // => '#5FB6AD'How do I export it as CSS, JSON, or a package?
Section titled “How do I export it as CSS, JSON, or a package?”Every exporter validates the family first and is deterministic. toCss is the stylesheet to ship (all modes, <name>-system, densities, responsive values); toThemeJson is the editable source that importTheme reads back; toFontsCss is the opt-in font loader. toPackageZip bundles all of them, plus a registration module, DTCG token files, and a compliance report, as a versioned npm package.
import { deriveFamily, importTheme, packageFiles, toCss, toPackageZip, toThemeJson } from '@absoluteui/theme/designer';
const family = deriveFamily({ name: 'acme-hr', seeds: { primary: '#0F766E' }, version: '1.2.0' });toCss(family).includes('[data-aui-theme="acme-hr-system"]'); // => trueimportTheme(toThemeJson(family)).ok; // => trueconst files = packageFiles(family, { packageName: '@acme/hr-theme' }).map((f) => f.path);files.slice(0, 6); // => ['package.json', 'README.md', 'index.js', 'index.d.ts', 'theme.css', 'fonts.css']const zip = toPackageZip(family, { packageName: '@acme/hr-theme' });String.fromCharCode(zip[0] ?? 0, zip[1] ?? 0); // => 'PK'In the application, import the package’s theme.css, call its registerAcmeHrTheme(), and set theme: 'acme-hr' (or acme-hr-system) in configuration.
How do I load a theme file someone uploaded?
Section titled “How do I load a theme file someone uploaded?”Treat theme files as untrusted. importTheme enforces a size limit, known token names, and a safe CSS value grammar, migrates schema version 1 themes, and returns every problem with its JSON path instead of throwing.
import { deriveFamily, importTheme } from '@absoluteui/theme/designer';
const family = deriveFamily({ name: 'acme-hr' });const light = family.modes.light;const upload = JSON.stringify({ ...family, modes: { ...family.modes, light: { ...light, tokens: { ...light.tokens, 'color-bg': 'url(https://evil.test/a.png)' } } } });const result = importTheme(upload);result.ok; // => falseresult.ok ? [] : result.errors.map((e) => e.path); // => ['modes.light.tokens.color-bg']What goes wrong?
Section titled “What goes wrong?”Exporters reject an invalid family with THEME_INVALID (names are lowercase kebab-case, versions are semantic versions). A derived family is compliant, but manual overrides can break it: never present a family whose report is not compliant as accessible, and verify rendered pages as well, because the assessment is token-level.
import { deriveFamily, toCss } from '@absoluteui/theme/designer';
toCss(deriveFamily({ name: 'Acme HR' })); // => throws THEME_INVALIDtoCss(deriveFamily({ name: 'acme-hr', version: 'v2' })); // => throws THEME_INVALIDAPI reference
Section titled “API reference”Functions
Section titled “Functions”oklchToRgb
Convert to sRGB, reducing chroma until the color is inside the sRGB gamut.
function oklchToRgb(color: Oklch): RGBA;parseOklch
function parseOklch(value: string): Oklch | null;rgbToOklch
function rgbToOklch(c: RGBA): Oklch;solveLightness
Move color along OKLCH lightness (keeping hue and chroma where the gamut allows)
until it reaches ratio against every backdrop. direction -1 darkens, +1 lightens.
Returns the first passing color, or the extreme (black/white end) when none passes.
function solveLightness(color: Oklch, backdrops: readonly RGBA[], ratio: number, direction: -1 | 1): RGBA;Constants
Section titled “Constants”toHex
const toHex: (c: RGBA) => string;toRgba
rgba() text with the given alpha, for translucent role tokens.
const toRgba: (c: RGBA, alpha: number) => string;Interfaces
Section titled “Interfaces”Oklch
interface Oklch { readonly l: number; readonly c: number; readonly h: number;}Compliance
Section titled “Compliance”Functions
Section titled “Functions”assessCompliance
Assess a theme family against every the specification category. Findings are sorted most urgent first.
function assessCompliance(family: ThemeFamily): ComplianceReport;toMs
function toMs(value: string | undefined): number | null;toPx
Length in CSS px (rem and em at a 16px root); null when the unit is not measurable.
function toPx(value: string | undefined): number | null;Constants
Section titled “Constants”categoryLabels
const categoryLabels: Readonly<Record<ComplianceCategory | 'contrast', string>>;Interfaces
Section titled “Interfaces”CategorySummary
interface CategorySummary { readonly category: ComplianceCategory | 'contrast'; readonly label: string; readonly passed: number; readonly failed: number; readonly warnings: number; readonly notMeasurable: number; readonly requiredFailures: number;}ComplianceFinding
interface ComplianceFinding { readonly id: string; readonly category: ComplianceCategory; readonly rule: string; readonly status: RuleStatus; readonly severity: Severity; readonly required: boolean; readonly mode: ThemeMode | 'all'; readonly message: string; readonly measured: string | null; readonly expected: string | null; readonly tokens: readonly string[]; readonly components: readonly string[]; readonly remediation: string | null;}ComplianceReport
interface ComplianceReport { readonly family: string; readonly compliant: boolean; readonly statement: string; readonly categories: readonly CategorySummary[]; readonly findings: readonly ComplianceFinding[];}ComplianceCategory
type ComplianceCategory = 'text-contrast' | 'large-text-contrast' | 'non-text-contrast' | 'focus-visibility' | 'state-differentiation' | 'target-size' | 'zoom-reflow' | 'reduced-motion' | 'high-contrast';RuleStatus
type RuleStatus = 'pass' | 'fail' | 'warn' | 'not-measurable';Derive
Section titled “Derive”Functions
Section titled “Functions”defaultFoundation
Non-color foundation tokens (typography, spacing, shape, motion) of the built-in family.
function defaultFoundation(): TokenMap;deriveFamily
A complete family derived from seeds (no component overrides, built-in densities).
function deriveFamily(options: DeriveFamilyOptions): ThemeFamily;deriveMode
Derive the complete token set of one mode from seeds and the (light) foundation. Dark mode deepens shadows; high contrast thickens borders and focus rings and drops shadows.
function deriveMode(seedMap: TokenMap, mode: ThemeMode, foundation?: TokenMap): TokenMap;normalizeSeeds
Seeds from a token map, filling unknown or unparseable entries from the defaults.
function normalizeSeeds(seeds: TokenMap): ThemeSeeds;overriddenTokens
Which tokens of a mode differ from what the seeds derive (the author’s manual overrides).
function overriddenTokens(family: ThemeFamily, mode: ThemeMode): readonly string[];ratioOf
Contrast ratio helper for UIs (two CSS colors; null when either cannot be parsed).
function ratioOf(foreground: string, background: string): number | null;Constants
Section titled “Constants”defaultSeeds
const defaultSeeds: ThemeSeeds;seedNames
const seedNames: readonly (keyof ThemeSeeds)[];Interfaces
Section titled “Interfaces”DeriveFamilyOptions
interface DeriveFamilyOptions { readonly name: string; readonly seeds?: TokenMap; readonly foundation?: TokenMap; readonly version?: string; readonly description?: string; readonly author?: string; readonly fonts?: readonly WebFont[];}ThemeSeeds
interface ThemeSeeds { readonly primary: string; readonly secondary: string; readonly accent: string; readonly success: string; readonly error: string; readonly warning: string; readonly info: string; readonly background: string; readonly surface: string;}Export
Section titled “Export”Functions
Section titled “Functions”componentOverrides
Component tokens set anywhere in a family, for summaries.
function componentOverrides(family: ThemeFamily): readonly { mode: ThemeMode; name: string; value: string; fallback: string;}[];dtcgPath
DTCG group and token name for a global token (color-text-muted -> color / text-muted).
function dtcgPath(name: string): [string, string];packageFiles
The files of a versioned theme package, before zipping.
function packageFiles(family: ThemeFamily, options?: PackageOptions): PackageFile[];toCss
CSS custom properties in the aui.theme cascade layer. Themes: <name>,
<name>-dark, <name>-high-contrast, and <name>-system (follows the OS color
scheme and contrast preference). Densities and responsive overrides are included.
Web fonts are not: they load from toFontsCss(), so this file never makes a
third-party request.
function toCss(family: ThemeFamily): string;toDtcg
DTCG design tokens for one mode (Format Module 2025.10; media type
application/design-tokens+json, extension .tokens.json). Component tokens that a
mode does not override are exported as aliases of their global fallback. Values
DTCG cannot express (for example % or calc()) are kept under $extensions.
function toDtcg(family: ThemeFamily, mode: ThemeMode): string;toFontsCss
Opt-in web font loader: @import rules for the family’s font stylesheets, with the
CSP hosts they need. Empty (comments only) when the family has no web fonts.
function toFontsCss(family: ThemeFamily): string;toPackageZip
Versioned theme package as zip bytes; entries sit under package/, like an npm tarball.
function toPackageZip(family: ThemeFamily, options?: PackageOptions): Uint8Array;toScss
SCSS maps: modes (tokens and component tokens), densities, responsive overrides.
function toScss(family: ThemeFamily): string;toThemeJson
Theme JSON (schema version 2), the format importTheme() reads back.
function toThemeJson(family: ThemeFamily): string;toTypeScript
TypeScript module: the family plus a function that registers its three themes at runtime.
function toTypeScript(family: ThemeFamily): string;Interfaces
Section titled “Interfaces”PackageFile
interface PackageFile { readonly path: string; readonly content: string;}PackageOptions
interface PackageOptions { readonly packageName?: string; readonly license?: string;}Import
Section titled “Import”Functions
Section titled “Functions”assertValidFamily
Validate a family built in memory (for example by a designer UI) before it is exported or applied. Throws a THEME_INVALID configuration error listing the issues.
function assertValidFamily(family: ThemeFamily): ThemeFamily;importTheme
Parse and validate theme file text.
function importTheme(textInput: string): ImportResult;importThemeValue
Validate an already-parsed value.
function importThemeValue(input: unknown): ImportResult;Constants
Section titled “Constants”MAX_IMPORT_BYTES
const MAX_IMPORT_BYTES = 1000000;Interfaces
Section titled “Interfaces”ImportIssue
interface ImportIssue { readonly path: string; readonly message: string;}ImportResult
type ImportResult = { readonly ok: true; readonly family: ThemeFamily; readonly format: 'family-v2' | 'theme-v1' | 'bundle-v1'; readonly warnings: readonly string[];} | { readonly ok: false; readonly errors: readonly ImportIssue[];};Functions
Section titled “Functions”builtInFamily
The built-in Absolute Default family (light, dark, high contrast), the designer’s starting point.
function builtInFamily(): ThemeFamily;familyToThemes
Flatten a family into schema-1 theme definitions that registerTheme() accepts.
function familyToThemes(family: ThemeFamily): ThemeDefinition[];modeThemeName
Runtime theme name of one mode (brand, brand-dark, brand-high-contrast).
function modeThemeName(familyName: string, mode: ThemeMode): string;resolveToken
The value a component token resolves to in a mode: its override, else its global fallback.
function resolveToken(mode: FamilyMode, name: string): string | undefined;Constants
Section titled “Constants”breakpointNames
const breakpointNames: readonly BreakpointName[];breakpoints
Same values as the SCSS $aui-breakpoints map.
const breakpoints: Readonly<Record<BreakpointName, number>>;componentTokenComponents
Components with component tokens, for UIs that group overrides by component.
const componentTokenComponents: readonly string[];densityNames
const densityNames: readonly DensityName[];FAMILY_SCHEMA_VERSION
const FAMILY_SCHEMA_VERSION = 2;familyModes
const familyModes: readonly ThemeMode[];RESPONSIVE_TOKEN_RE
Tokens a responsive (min-width) override may change: typography, spacing, layout widths.
const RESPONSIVE_TOKEN_RE: RegExp;Interfaces
Section titled “Interfaces”FamilyMode
interface FamilyMode { readonly tokens: TokenMap; readonly componentTokens: TokenMap;}ThemeFamily
interface ThemeFamily { readonly schemaVersion: typeof FAMILY_SCHEMA_VERSION; readonly name: string; readonly version: string; readonly description: string; readonly author: string; readonly seeds: TokenMap; readonly fonts: readonly WebFont[]; readonly modes: Readonly<Record<ThemeMode, FamilyMode>>; readonly densities: Readonly<Record<DensityName, TokenMap>>; readonly responsive: Readonly<Partial<Record<BreakpointName, TokenMap>>>;}BreakpointName
type BreakpointName = 'sm' | 'md' | 'lg' | 'xl';DensityName
type DensityName = 'compact' | 'spacious';Schema
Section titled “Schema”Functions
Section titled “Functions”themeJsonSchema
function themeJsonSchema(): Record<string, unknown>;Constants
Section titled “Constants”THEME_SCHEMA_ID
const THEME_SCHEMA_ID: string;Token usage
Section titled “Token usage”Constants
Section titled “Constants”tokenUsage
Components whose stylesheets read each token (the specification affected components).
const tokenUsage: Readonly<Record<string, readonly string[]>>;Functions
Section titled “Functions”crc32
function crc32(bytes: Uint8Array): number;createZip
function createZip(entries: readonly ZipEntry[]): Uint8Array;readZip
Read a stored-only zip produced by createZip (for tests and round trips).
function readZip(zip: Uint8Array): Map<string, Uint8Array>;Interfaces
Section titled “Interfaces”ZipEntry
Minimal ZIP writer (PKWARE APPNOTE 6.3.x): stored (uncompressed) entries, UTF-8 names, fixed timestamps so the same input always produces the same bytes. Enough for small theme packages without a dependency.
interface ZipEntry { readonly path: string; readonly content: string | Uint8Array;}