# How DESIGN.md Token Reference Resolution Works: The `{path.to.token}` Syntax Explained

> Understand DESIGN.md token reference resolution. Learn how the {path.to.token} syntax works with its three-phase algorithm for efficient value substitution.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: deep-dive
- Published: 2026-06-27

---

**DESIGN.md resolves token references using a three-phase algorithm that first parses primitive values into a symbol table, then recursively resolves chained references with cycle detection and depth limits, and finally substitutes concrete values into component definitions.**

The [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) CLI processes design tokens defined in YAML front-matter, where values can reference other tokens using the `{path.to.token}` syntax. This resolution mechanism allows design systems to create reusable, chained token values while preventing infinite loops and handling unresolved references.

## The Three Phases of Token Resolution

The resolver operates sequentially through three distinct phases implemented in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts).

### Phase 1: Initial Primitive Parsing

During the first pass, the `ModelHandler.execute` method (lines 55-74) parses all non-reference values and populates a **symbol table**. Primitive values such as colors, typography, and dimensions are stored as resolved objects, while raw reference strings remain unprocessed.

```typescript
// From handler.ts lines 55-74
symbolTable.set(`colors.${name}`, resolved);    // primitive color stored as object
symbolTable.set(`rounded.${name}`, raw);       // raw reference string stored as-is

```

This symbol table serves as the lookup registry for all subsequent resolution steps.

### Phase 2: Chained Reference Resolution

After primitive parsing, the system resolves raw reference strings using the `resolveReference` function (lines 73-96 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)). This recursive algorithm handles chained references—where one token points to another—until a concrete value is reached.

```typescript
function resolveReference(symbolTable, path, visited, depth = 0) {
  if (depth > MAX_REFERENCE_DEPTH) return null;     // depth limit guard
  if (visited.has(path)) return null;               // circular reference guard
  visited.add(path);
  
  const value = symbolTable.get(path);
  if (typeof value === 'string' && isTokenReference(value)) {
    const innerPath = value.slice(1, -1);           // strip curly braces
    return resolveReference(symbolTable, innerPath, visited, depth + 1);
  }
  return value;
}

```

The resolver implements two critical safety mechanisms:
- **Depth limiting** via `MAX_REFERENCE_DEPTH` to prevent infinite recursion
- **Cycle detection** using a `visited` Set to catch circular token references

### Phase 3: Component Construction

In the final phase (lines 84-101 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)), component definitions are processed. Each property containing a token reference is evaluated using `resolveReference`. If resolution fails, the reference is recorded for error reporting.

```typescript
// From handler.ts lines 84-101
if (isTokenReference(rawValue)) {
  const refPath = rawValue.slice(1, -1);
  const resolved = resolveReference(symbolTable, refPath, new Set());
  if (resolved !== null) properties.set(propName, resolved);
  else unresolvedRefs.push(rawValue);    // track unresolvable references
}

```

## Recursive Resolution Algorithm and Safety Guards

The `resolveReference` function in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts) follows a strict recursive pattern to traverse token chains. When encountering a reference string like `{colors.primary-60}`, the algorithm strips the curly braces and queries the symbol table for the inner path. If the retrieved value is another reference string, the function calls itself with an incremented depth counter.

The **circular guard** uses a `visited` Set that tracks all paths encountered during the current resolution chain. If a path appears twice in the same chain, the function returns `null` immediately. Similarly, exceeding `MAX_REFERENCE_DEPTH` triggers an early termination, ensuring the CLI cannot enter infinite loops through malformed token definitions.

## Practical Example: Resolving Component Tokens

Consider this [`DESIGN.md`](https://github.com/google-labs-code/design.md/blob/main/DESIGN.md) front-matter excerpt:

```yaml
---
colors:
  primary-60: "#0D47A1"
  primary-20: "#1976D2"
typography:
  label-md:
    fontFamily: Public Sans
    fontSize: 14px
components:
  button-primary:
    backgroundColor: "{colors.primary-60}"
    textColor: "{colors.primary-20}"
    typography: "{typography.label-md}"
---

```

The resolution process follows this sequence:

1. **Parse primitives**: `colors.primary-60`, `colors.primary-20`, and `typography.label-md` are parsed into concrete objects and stored in the symbol table.
2. **Resolve references**: `button-primary.backgroundColor` triggers `resolveReference(symbolTable, "colors.primary-60", new Set())`, which returns the color object `#0D47A1`.
3. **Construct model**: The component object is populated with fully resolved values, ready for export to Tailwind CSS or DTCG format.

## Implementation Files and Specification Reference

The token reference behavior is formally defined in two key locations:

- **[`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md)** (lines 86-87): Specifies that token references must be wrapped in curly braces and point to primitive values, except within the `components` section where composite values are permitted.
- **[`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts)**: Contains the complete resolution implementation, including `ModelHandler.execute`, the `resolveReference` recursive function, and the component construction logic.

According to the specification in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md), the `{path.to.token}` syntax requires the inner path to resolve to a primitive design token value, ensuring type consistency across the design system.

## Summary

- **Three-phase resolution**: Primitive parsing populates the symbol table, chained references are resolved recursively, and components are constructed with concrete values.
- **Safety guards**: The `resolveReference` function uses `MAX_REFERENCE_DEPTH` and a `visited` Set to prevent infinite loops and detect circular dependencies.
- **Unresolvable tracking**: Failed references are collected in `unresolvedRefs` for diagnostic reporting during the CLI linting process.
- **Spec compliance**: The official specification in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) defines the syntax constraints and value requirements for token references.

## Frequently Asked Questions

### What is the maximum depth for token reference chains in DESIGN.md?

The resolution algorithm enforces a maximum depth limit through the `MAX_REFERENCE_DEPTH` constant defined in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts). While the exact numeric value is implementation-dependent, the guard prevents infinite recursion by terminating resolution attempts that exceed this threshold, returning `null` for excessively deep chains.

### How does DESIGN.md detect circular token references?

Circular references are detected using a `visited` Set that tracks all paths encountered during the current resolution chain. In the `resolveReference` function (lines 73-96 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)), if a path already exists in the `visited` Set, the function immediately returns `null`, breaking the cycle and preventing infinite recursion.

### Where is the official token reference syntax defined?

The official specification for the `{path.to.token}` syntax appears in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) at lines 86-87. This document specifies that references must be wrapped in curly braces and typically resolve to primitive values, with the exception of component definitions where composite values are permitted.

### What happens when a token reference cannot be resolved?

When `resolveReference` returns `null` for a reference—either due to a missing token, circular dependency, or depth limit violation—the unresolved reference string is pushed to the `unresolvedRefs` array (lines 84-101 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)). The CLI subsequently reports these unresolved references to help developers identify missing or invalid token definitions.