Deep Dive: Resolver Module

Why a Resolver Module?

Simple token systems resolve references by direct substitution: {core.color.blue.500} becomes #0a65fe. But real-world systems need more: brand switching, theme variants, platform-specific values, and conditional overrides.

The DTCG 1.0 Resolver Module provides a standardized approach to context-aware token resolution. It defines how token sets combine, which modifiers apply, and in what order conflicts are resolved.

Resolver Document Structure

A resolver document is a JSON file that defines the resolution strategy for a token system:

{
  "$schema": "resolver.schema.json",
  "name": "Portfolio Design System Resolver",
  "version": "2025-01-01",
  "description": "Context-aware token resolution",
  
  "sets": { ... },           // Token collections
  "modifiers": { ... },      // Value transformations
  "resolutionOrder": [ ... ] // Precedence rules
}

Core Properties

PropertyPurpose
setsNamed collections of tokens that can be combined
modifiersTransformations applied to resolved values
resolutionOrderSequence determining which values win conflicts

Sets: Token Collections

Sets define collections of tokens that can be combined during resolution. Each set can reference external files or define inline tokens.

File-Based Sets

{
  "sets": {
    "foundation": {
      "description": "Core design tokens",
      "sources": [
        { "$ref": "core.tokens.json" }
      ]
    },
    "semantic": {
      "description": "Semantic tokens referencing foundation",
      "sources": [
        { "$ref": "semantic.tokens.json" }
      ]
    },
    "brand-acme": {
      "description": "Acme brand overrides",
      "sources": [
        { "$ref": "brands/acme.tokens.json" }
      ]
    }
  }
}

Inline Sets

For small overrides or runtime values, sets can define tokens inline:

{
  "sets": {
    "runtime-overrides": {
      "description": "Dynamic runtime values",
      "tokens": {
        "color": {
          "brand": {
            "primary": {
              "$type": "color",
              "$value": "#custom-brand-color"
            }
          }
        }
      }
    }
  }
}

Set Composition

Sets can reference other sets, building layers of tokens:

{
  "sets": {
    "foundation": {
      "sources": [{ "$ref": "core.tokens.json" }]
    },
    "semantic": {
      "sources": [
        { "$ref": "#/sets/foundation" },
        { "$ref": "semantic.tokens.json" }
      ]
    },
    "brand-complete": {
      "sources": [
        { "$ref": "#/sets/semantic" },
        { "$ref": "brand.tokens.json" }
      ]
    }
  }
}

Modifiers: Context Dimensions

Modifiers define dimensions of variation like theme, platform, or accessibility settings. Each modifier has contexts that can be activated.

Theme Modifier

{
  "modifiers": {
    "theme": {
      "description": "Color theme (light/dark)",
      "default": "light",
      "contexts": {
        "light": {
          "description": "Light theme",
          "sources": [
            { "$ref": "#/sets/foundation" },
            { "$ref": "#/sets/semantic" }
          ]
        },
        "dark": {
          "description": "Dark theme",
          "sources": [
            { "$ref": "#/sets/foundation" },
            { "$ref": "#/sets/semantic" },
            { "$ref": "themes/dark.tokens.json" }
          ]
        }
      }
    }
  }
}

Platform Modifier

{
  "modifiers": {
    "platform": {
      "description": "Target platform",
      "default": "web",
      "contexts": {
        "web": {
          "sources": [{ "$ref": "platforms/web.tokens.json" }]
        },
        "ios": {
          "sources": [{ "$ref": "platforms/ios.tokens.json" }]
        },
        "android": {
          "sources": [{ "$ref": "platforms/android.tokens.json" }]
        }
      }
    }
  }
}

Accessibility Modifier

{
  "modifiers": {
    "accessibility": {
      "description": "Accessibility preferences",
      "default": "default",
      "contexts": {
        "default": {
          "sources": []
        },
        "high-contrast": {
          "sources": [{ "$ref": "a11y/high-contrast.tokens.json" }]
        },
        "reduced-motion": {
          "sources": [{ "$ref": "a11y/reduced-motion.tokens.json" }]
        }
      }
    }
  }
}

Resolution Order

The resolution order defines precedence when multiple sets define the same token. Later entries override earlier ones.

Basic Resolution Order

{
  "resolutionOrder": [
    { "$ref": "#/sets/foundation" },
    { "$ref": "#/sets/semantic" },
    { "$ref": "#/modifiers/theme" }
  ]
}

This means:

  1. Foundation tokens load first (base values)
  2. Semantic tokens override foundation where they conflict
  3. Theme modifier applies last, overriding both

Complex Resolution Order

{
  "resolutionOrder": [
    { "$ref": "#/sets/foundation" },
    { "$ref": "#/sets/semantic" },
    { "$ref": "#/modifiers/platform" },
    { "$ref": "#/modifiers/theme" },
    { "$ref": "#/modifiers/accessibility" },
    { "$ref": "#/sets/brand-overrides" }
  ]
}

Resolution Contexts

When resolving tokens, you provide a context that specifies which modifier values to use:

// Resolution context
const context = {
  theme: "dark",
  platform: "ios",
  accessibility: "high-contrast"
};

// Resolver applies context to resolution order
const resolvedTokens = resolver.resolve(context);

Context Examples

ScenarioContext
Web, light theme{ platform: "web", theme: "light" }
iOS, dark theme{ platform: "ios", theme: "dark" }
High contrast mode{ accessibility: "high-contrast" }
Brand A, dark, Android{ brand: "acme", theme: "dark", platform: "android" }

JSON Pointer References

The resolver uses JSON Pointer syntax for precise references within and across documents:

Internal References

// Reference a set defined in the same document
{ "$ref": "#/sets/foundation" }

// Reference a modifier context
{ "$ref": "#/modifiers/theme/contexts/dark" }

External References

// Reference an external file
{ "$ref": "core.tokens.json" }

// Reference a specific path in an external file
{ "$ref": "core.tokens.json#/color/palette/blue" }

Our Resolver Configuration

Here's our actual resolver configuration that handles foundation, semantic, and theme contexts:

// ui/designTokens/resolver.json
{
  "$schema": "../utils/designTokens/validators/resolver.schema.json",
  "name": "Portfolio Design System Resolver",
  "version": "2025-01-01",
  
  "sets": {
    "foundation": {
      "description": "Core design tokens",
      "sources": [{ "$ref": "core.tokens.json" }]
    },
    "semantic": {
      "description": "Semantic tokens",
      "sources": [{ "$ref": "semantic.tokens.json" }]
    }
  },
  
  "modifiers": {
    "theme": {
      "description": "Color theme modifier",
      "default": "light",
      "contexts": {
        "light": {
          "description": "Light theme",
          "sources": [
            { "$ref": "#/sets/foundation" },
            { "$ref": "#/sets/semantic" }
          ]
        },
        "dark": {
          "description": "Dark theme",
          "sources": [
            { "$ref": "#/sets/foundation" },
            { "$ref": "#/sets/semantic" }
          ]
        }
      }
    }
  },
  
  "resolutionOrder": [
    { "$ref": "#/sets/foundation" },
    { "$ref": "#/sets/semantic" },
    { "$ref": "#/modifiers/theme" }
  ]
}

Build System Integration

The resolver integrates with the build system to generate context-specific outputs:

// Build configuration
export default {
  resolver: './ui/designTokens/resolver.json',
  
  outputs: [
    {
      context: { theme: 'light' },
      css: './dist/tokens-light.css'
    },
    {
      context: { theme: 'dark' },
      css: './dist/tokens-dark.css'
    },
    {
      context: { theme: 'light', platform: 'ios' },
      swift: './dist/Tokens.swift'
    }
  ]
};

Runtime Resolution

import { Resolver } from '@/utils/designTokens/resolver-module';
import resolverConfig from '@/ui/designTokens/resolver.json';

// Create resolver instance
const resolver = new Resolver(resolverConfig);

// Resolve for specific context
const lightTokens = resolver.resolve({ theme: 'light' });
const darkTokens = resolver.resolve({ theme: 'dark' });

// Get specific token value
const primaryColor = resolver.getToken(
  'semantic.color.foreground.primary',
  { theme: 'dark' }
);

Pitfalls to Avoid

1. Circular Set References

Sets cannot reference themselves or form cycles. The resolver detects and rejects circular dependencies.

// BAD: Circular reference
{
  "sets": {
    "a": { "sources": [{ "$ref": "#/sets/b" }] },
    "b": { "sources": [{ "$ref": "#/sets/a" }] }
  }
}

// GOOD: Linear dependency chain
{
  "sets": {
    "foundation": { "sources": [{ "$ref": "core.tokens.json" }] },
    "semantic": { "sources": [
      { "$ref": "#/sets/foundation" },
      { "$ref": "semantic.tokens.json" }
    ]}
  }
}

2. Missing Default Contexts

Every modifier should have a default context. Without it, resolution fails when no context is provided.

// BAD: No default
{
  "modifiers": {
    "theme": {
      "contexts": {
        "light": { ... },
        "dark": { ... }
      }
    }
  }
}

// GOOD: Default specified
{
  "modifiers": {
    "theme": {
      "default": "light",
      "contexts": {
        "light": { ... },
        "dark": { ... }
      }
    }
  }
}

3. Inconsistent Token Coverage

All contexts within a modifier should define the same tokens. Otherwise, some contexts will have missing values.

// BAD: Dark theme missing tokens
{
  "contexts": {
    "light": {
      "sources": [
        { "$ref": "colors.json" },
        { "$ref": "spacing.json" }
      ]
    },
    "dark": {
      "sources": [
        { "$ref": "colors-dark.json" }
        // Missing spacing!
      ]
    }
  }
}

// GOOD: Consistent coverage
{
  "contexts": {
    "light": {
      "sources": [
        { "$ref": "#/sets/foundation" },
        { "$ref": "#/sets/semantic" }
      ]
    },
    "dark": {
      "sources": [
        { "$ref": "#/sets/foundation" },
        { "$ref": "#/sets/semantic" },
        { "$ref": "themes/dark-overrides.json" }
      ]
    }
  }
}

4. Wrong Resolution Order

Resolution order matters. More specific overrides should come later. Foundation before semantic, semantic before brand.

// BAD: Brand before semantic (brand values get overwritten)
{
  "resolutionOrder": [
    { "$ref": "#/sets/foundation" },
    { "$ref": "#/sets/brand" },
    { "$ref": "#/sets/semantic" }
  ]
}

// GOOD: Most specific last
{
  "resolutionOrder": [
    { "$ref": "#/sets/foundation" },
    { "$ref": "#/sets/semantic" },
    { "$ref": "#/sets/brand" }
  ]
}

Summary

  • Sets define collections of tokens that can be combined
  • Modifiers define variation dimensions (theme, platform, accessibility)
  • Resolution order determines which values win conflicts
  • Contexts specify which modifier values to use during resolution
  • JSON Pointers enable precise internal and external references
  • Build integration generates context-specific outputs

Next Steps

The resolver module works alongside multi-brand theming to enable complex scenarios. For simpler setups, the $extensions.design.paths approach in core vs semantic may be sufficient. See schema validation for how resolver documents are validated.

Source files:

  • ui/designTokens/resolver.json — Our resolver configuration
  • utils/designTokens/utils/resolver-module.ts — Resolver implementation
  • utils/designTokens/validators/resolver.schema.json — JSON Schema
← Back to Design Tokens