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
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.
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});2Expressions
Token values containing only numbers, operators, and units are recognized as expressions. The expression evaluator supports +, -, *, /, and parentheses. Units are preserved through evaluation.
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
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.
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)auto() recognizes the *Foreground and *Fg suffixes. Any token with one of these suffixes will automatically resolve against its base color.4Resolution order
When resolveTokens() is called, it processes the token tree in this order:
- 1Base tokens— Literal values from the theme definition are collected into a flat lookup map.
- 2Extends merge— The parent theme's tokens are merged underneath — child values override parent values.
- 3Overrides— Family-scoped or runtime overrides are applied last in the merge.
- 4References— $ref and {ref} tokens are resolved by walking the flat map, with cycle detection.
- 5Expressions— Numeric expressions (16 + 8, 2 * 4px) are evaluated and replaced with computed values.
- 6Derived tokens— auto() and contrast() calls compute final values using already-resolved backgrounds.
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
resolveTokens(tokens)
Takes a ThemeTokens object and returns a new object with all references, expressions, and derived tokens fully resolved.
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.
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.
1import { hasTokenReferences } from "@theme-kit/core";
2
3hasTokenReferences("$colors.primary"); // true
4hasTokenReferences("{spacing.sm}"); // true
5hasTokenReferences("8px + 4px"); // false
6hasTokenReferences("#3b82f6"); // falseevaluateExpression(expr)
Evaluates a numeric expression string and returns the result. If the input is not a valid expression, it is returned unchanged.
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)