Skip to content

Resolution

Token resolution

Theme Kit tokens are more than static key-value pairs. A reference like $colors.primary is resolved at build time, expressions like 2 * 16px are evaluated, and derived tokens like auto() produce contrast-safe foreground colors automatically.

1Token references

Reference tokens let one token alias another, keeping your palette DRY and your overrides shallow.

Use $ prefix or {braces} syntax to reference another token by its flattened path. References are resolved after the base theme and extends are merged, so you can override a single source and every reference follows.

core — token references
ts
1import { defineTheme } from "@theme-kit/core";
2
3const theme = defineTheme({
4  name: "brand",
5  tokens: {
6    colors: {
7      primary: "#3b82f6",
8      // Reference another token with $ prefix
9      primaryHover: "$colors.primary",
10      // Or use the brace syntax
11      primaryMuted: "{colors.primary}",
12    },
13    spacing: {
14      sm: "8px",
15      md: "16px",
16      lg: "$spacing.sm + $spacing.md", // 24px
17    },
18  },
19});
Circular references throw |If token A references token B which references token A, the resolver detects the cycle and throws an error with the full reference chain.

2Expressions

Numeric expressions let you compute spacing, sizes, and other values inline without a separate build step.

Token values containing only numbers, operators, and units are recognized as expressions. The expression evaluator supports +, -, *, /, and parentheses. Units are preserved through evaluation.

core — expressions
ts
1import { evaluateExpression, isExpression } from "@theme-kit/core";
2
3// Numeric expressions with +, -, *, / and parentheses
4isExpression("16 + 8");        // true
5isExpression("2 * (12 + 4)");  // true
6isExpression("#3b82f6");       // false
7
8// Evaluate resolves the expression, preserving units
9evaluateExpression("8 + 16");         // "24"
10evaluateExpression("4 * 4px");        // "16px"
11evaluateExpression("(100 - 20)%");    // "80%"
12evaluateExpression("2 * (12 + 4)px"); // "32px"
13
14// Used inside token definitions
15const theme = defineTheme({
16  name: "computed",
17  tokens: {
18    spacing: {
19      base: "4px",
20      double: "2 * $spacing.base", // 8px
21      triple: "3 * $spacing.base", // 12px
22    },
23  },
24});

3Derived colors

Derived tokens compute values like contrast-safe foreground colors from a background, no manual pairing needed.

The auto() function infers which background token to use based on the token's own name — a token named primaryForeground looks up primary and returns black or white for WCAG-safe contrast. You can also call contrast() explicitly with a hex value.

core — derived colors
ts
1import { defineTheme, contrast, auto } from "@theme-kit/core";
2
3const theme = defineTheme({
4  name: "auto-contrast",
5  tokens: {
6    colors: {
7      primary: "#3b82f6",
8      // auto() infers the path from the token name
9      // "primaryForeground" → looks up "primary" → returns black or white
10      primaryForeground: "auto()",
11
12      surface: "#f8fafc",
13      surfaceForeground: "auto()",
14
15      danger: "#ef4444",
16      dangerForeground: "auto()",
17    },
18  },
19});
20
21// Or use contrast() with an explicit color
22const manual = defineTheme({
23  name: "manual-contrast",
24  tokens: {
25    colors: {
26      primary: "#3b82f6",
27      primaryForeground: "contrast(#3b82f6)", // → "#000000"
28    },
29  },
30});
31
32// Direct utility usage
33contrast("#3b82f6"); // "#000000" (luminance > 0.179)
34contrast("#000000"); // "#ffffff" (luminance <= 0.179)
Naming convention |auto() recognizes the *Foreground and *Fg suffixes. Any token with one of these suffixes will automatically resolve against its base color.

4Resolution order

The resolver runs a strict pipeline so every token category is fully resolved before the next.

When resolveTokens() is called, it processes the token tree in this order:

Pipeline
  1. 1
    Base tokensLiteral values from the theme definition are collected into a flat lookup map.
  2. 2
    Extends mergeThe parent theme's tokens are merged underneath — child values override parent values.
  3. 3
    OverridesFamily-scoped or runtime overrides are applied last in the merge.
  4. 4
    References$ref and {ref} tokens are resolved by walking the flat map, with cycle detection.
  5. 5
    ExpressionsNumeric expressions (16 + 8, 2 * 4px) are evaluated and replaced with computed values.
  6. 6
    Derived tokensauto() and contrast() calls compute final values using already-resolved backgrounds.
core — resolution order
ts
1import { resolveTokens } from "@theme-kit/core";
2
3// Given a theme that uses all three mechanisms:
4const theme = {
5  colors: {
6    // 1. Literal value
7    primary: "#3b82f6",
8
9    // 2. Reference — resolved after base/extends
10    hover: "$colors.primary",
11
12    // 3. Expression — evaluated after references resolve
13    lightened: "#3b82f6 + #111111",
14
15    // 4. Derived — computed last
16    primaryForeground: "auto()",
17  },
18};
19
20const resolved = resolveTokens(theme);
21// resolved.colors.primary         → "#3b82f6"
22// resolved.colors.hover           → "#3b82f6"
23// resolved.colors.lightened       → "#4c9307" (numeric hex add)
24// resolved.colors.primaryForeground → "#000000"

5API Reference

The resolve module exports focused utilities you can use individually or compose into your own pipeline.

resolveTokens(tokens)

Takes a ThemeTokens object and returns a new object with all references, expressions, and derived tokens fully resolved.

resolveTokens()
ts
1import { resolveTokens } from "@theme-kit/core";
2
3const tokens = {
4  colors: {
5    primary: "#3b82f6",
6    hover: "$colors.primary",
7    foreground: "auto()",
8  },
9  spacing: {
10    sm: "8px",
11    md: "16px",
12    lg: "$spacing.sm + $spacing.md",
13  },
14};
15
16const resolved = resolveTokens(tokens);
17// All references, expressions, and derived values resolved
18// resolved.colors.hover        → "#3b82f6"
19// resolved.colors.foreground   → "#000000"
20// resolved.spacing.lg          → "24"

flattenTokens(tokens)

Flattens a nested ThemeTokens object into a dot-separated flat map. Useful for building lookup tables or custom resolution logic.

flattenTokens()
ts
1import { flattenTokens } from "@theme-kit/core";
2
3const tokens = {
4  colors: {
5    primary: { default: "#3b82f6", hover: "#2563eb" },
6    surface: "#f8fafc",
7  },
8  spacing: { sm: "8px" },
9};
10
11const flat = flattenTokens(tokens);
12// {
13//   "colors.primary.default": "#3b82f6",
14//   "colors.primary.hover":    "#2563eb",
15//   "colors.surface":          "#f8fafc",
16//   "spacing.sm":              "8px",
17// }

hasTokenReferences(value)

Returns true if the string contains a $ or {} token reference.

hasTokenReferences()
ts
1import { hasTokenReferences } from "@theme-kit/core";
2
3hasTokenReferences("$colors.primary");   // true
4hasTokenReferences("{spacing.sm}");      // true
5hasTokenReferences("8px + 4px");         // false
6hasTokenReferences("#3b82f6");           // false

evaluateExpression(expr)

Evaluates a numeric expression string and returns the result. If the input is not a valid expression, it is returned unchanged.

evaluateExpression()
ts
1import { evaluateExpression } from "@theme-kit/core";
2
3evaluateExpression("8 + 16");          // "24"
4evaluateExpression("2 * (12 + 4)px"); // "32px"
5evaluateExpression("(100 - 20)%");    // "80%"
6evaluateExpression("#3b82f6");        // "#3b82f6" (not an expression)
Token Resolution — Theme Kit