Theme Engine: ref() token references not resolved in ejected CSS

Resolved 💬 4 comments Opened Dec 17, 2025 by jonathan-oron Closed Feb 27, 2026

Bug Description

The ref() helper function in @theme-engine/helpers/define-theme creates token references (e.g., $global.primaryColor) that are not properly resolved when generating static CSS via the eject CLI.

Expected Behavior

When using ref() to reference another token:

tokens: {
  'global.primaryColor': '#D49653',
  'tooltip.bg': ref('global.primaryColor'),  // Creates '$global.primaryColor'
}

The ejected CSS should output either:

  1. Resolved value: --tooltip-bg: #D49653;
  2. CSS var reference: --tooltip-bg: var(--global-primaryColor);

Actual Behavior

The ejected CSS outputs the raw reference string, which is invalid CSS:

--tooltip-bg: $global.primaryColor;

Root Cause

The BaseResolver.handleCheckExplicit() method in engine/core/base-resolver.ts:210-226 returns explicit values as-is without checking if they are $tokenPath references that need further resolution:

protected handleCheckExplicit(ctx: ResolutionContext<TValue>, frame: ResolutionFrame): void {
  const explicitValue = ctx.values[frame.path];
  if (explicitValue !== null && explicitValue !== undefined) {
    // Returns value as-is, even if it's a '$...' reference
    ctx.results.set(frame.path, explicitValue);
    ctx.stack.pop();
    return;
  }
  // ...
}

The CSS generator in engine/eject/generate-css.ts:72 also outputs values without transformation:

lines.push(`${indent}${cssVarName}: ${value};${newline}`);

Steps to Reproduce

  1. Create a theme using ref():
import { defineTheme, ref } from '../engine/helpers/define-theme';

export default defineTheme({
  $meta: { id: 'test', name: 'Test', version: '1.0.0' },
  tokens: {
    'global.primaryColor': '#D49653',
    'tooltip.bg': ref('global.primaryColor'),
  },
  behaviors: {},
});
  1. Run the eject CLI:
npx tsx packages/theme-engine/cli/eject.ts -t path/to/theme.ts -o /tmp/test
  1. Check the output CSS - it contains $global.primaryColor instead of a valid value.

Affected Files

  • packages/theme-engine/engine/core/base-resolver.ts - Resolver doesn't expand $ references
  • packages/theme-engine/engine/eject/generate-css.ts - CSS generator doesn't transform $ to var()
  • packages/theme-engine/engine/helpers/define-theme.ts - Documents ref() but feature is incomplete

Suggested Fix Options

Option A: Resolve at build time

Modify the resolver to detect $tokenPath values and resolve them to concrete values by looking up the referenced token.

Option B: Convert to CSS var()

Modify the CSS generator to transform $token.pathvar(--token-path) for runtime resolution.

Option C: Hybrid approach

  • For eject (static CSS): Resolve to concrete values
  • For runtime: Convert to CSS var() references

Workaround

Until fixed, avoid using ref() and instead:

  1. Rely on fallback chains (which work correctly)
  2. Use hardcoded values for tokens without applicable fallbacks

Environment

  • Package: @theme-engine
  • File: packages/theme-engine/themes/stunning.theme.ts

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗