../work

Variable Design Standard

Open specification for design variable governance. Defines JSON structure, naming rules, validation, and versioning on DTCG 2025.10 format. Three layers. No mapped collections.

Govern variables across brands

After building Aurora at GM across 4 brands, I wanted a governance layer on an existing format that could handle multiple brands without adding complexity. The JSON file set is the API surface: file paths and variable names form the contract, and a rename is a breaking change.

Naming, handoff, and layer proliferation

Teams use different terms: design tokens, style properties, variables, constants. The terminology split is a symptom of a deeper problem: there is no standard for how design variables should be governed across tools and teams. This creates:

  • Naming confusion: Designers call them one thing, developers another. Handoff breaks because vocabulary is split.

  • No governance: Variables change without rules. Breaking changes ship to production without notice.

  • Tool lock-in: Figma Variables, Tokens Studio, and code-based systems use incompatible formats and reference syntax.

  • Handoff breaks: Design and code don't align. No way to track which variable version is deployed.

  • No validation: Invalid variables pass review and cause build failures downstream.

  • Layer proliferation: Teams add mapped collections between base and semantic layers to solve brand switching. Each extra layer means extra reference hops, extra naming decisions, and extra surfaces to audit. Variables should be easy to reason about, without requiring a specialist to untangle.

My role
Specification Author & Editor
Timeline
2024 - Present
Built with
JSON SchemaDTCG 2025.10Design TokensVersion ControlCI/CD ValidationStyle Dictionary
In this case study

Treat variable names as an API

Variable Design Standard (VDS) is a governance layer on the DTCG 2025.10 format. It defines naming rules, validation requirements, versioning strategy, and adapter patterns for handoff as teams and products grow. It uses three layers and selects files for each brand, without extra reference hops.

VDS defines three layers only. Base, Alias, Component. Implementations must not add a fourth layer between Alias and Base:

  • Base variables: Raw scales and palettes. The only place literal values live. color.gray.900, space.4

  • Alias variables: Semantic intent. Reference base variables. color.text.primary → {color.gray.900}

  • Component variables: Scoped control when needed. component.button.color.background → {color.surface.brand}

Tool adapters

Tool exports are inputs, not the contract. Adapters normalize Figma, Tokens Studio, and other formats into VDS:

Governance checks

Variable names are treated like an API. Changes are reviewed before merge:

  • Naming rules validated in CI (dot-separated paths, no hyphens)
  • References validated for resolution and cycles
  • Breaking changes require MAJOR version bump
  • Deprecation strategy with migration paths
JSON contract and adapter examples

JSON-as-API Architecture

json
{
  "color": {
    "text": {
      "primary": {
        "$type": "color",
        "$value": "{color.gray.900}",
        "$description": "Primary text color for body content"
      }
    },
    "surface": {
      "brand": {
        "$type": "color",
        "$value": "{color.brand.primary}"
      }
    }
  }
}

Adapter examples

json
// Before: Figma export (tool-specific)
{
  "@primitives": {
    "$color": {
      "primary": {
        "$type": "color",
        "$value": "{@primitives.$color.blue.500}",
        "$variable_metadata": {
          "figmaId": "VariableID:502:227"
        }
      }
    }
  }
}
json
// After: VDS contract (normalized)
{
  "primitives": {
    "color": {
      "primary": {
        "$type": "color",
        "$value": "{primitives.color.blue.500}",
        "$extensions": {
          "figma": { "variableId": "VariableID:502:227" }
        }
      }
    }
  }
}

Select files instead of adding mapped layers

Some approaches add mapped collections between base and semantic layers to handle brand switching. As those collections multiply, tracing the value that renders can require a specialist.

In VDS, file selection replaces mapped layers. JSON provides structure, folders organize each brand's values, and CSS @layer, @import, and the cascade handle consumption.

Brand selection happens by file list, not by mapped collections in a design tool panel. Selecting a brand folder changes the values while preserving the semantic names.

Brand file selection

text
tokens/
base/              # Shared scales (all brands)
  color.json
  spacing.json
brand-a/           # Brand A values
  color.json       # color.brand.primary = #0066cc
brand-b/           # Brand B values
  color.json       # color.brand.primary = #ff0066
Mapped layer costs and brand-selection examples

Mapped layer costs

Each additional layer between base and alias creates compounding overhead:

  • Second naming decision: Every semantic variable now requires naming the intermediate mapped value too. Double the naming surface. Double the review scope.

  • Extra reference hop: Every resolution traverses one more layer. More hops, more failure points, slower builds.

  • Extra audit surface: Another collection to review, validate, and keep in sync across brands. Another place for drift.

  • Expanded decision surface: Instead of one file selection, you now manage collection modes in cramped tool panels.

  • Artificial complexity: Complexity that benefits specialists, not teams. Variables should be easy to reason about.

json
// Non-conformant: Mapped layer adds overhead
{
  "mapped": {
    "brand": {
      "primary": { "$type": "color", "$value": "{color.blue.500}" }
    }
  },
  "color": {
    "surface": {
      "brand": { "$type": "color", "$value": "{mapped.brand.primary}" }
    }
  }
}
json
// Build config selects brand by source list
{
  "source": [
    "tokens/base/**/*.json",
    "tokens/brand-a/**/*.json"
  ]
}

If you can select files, you can switch brands. No extra collection required. One decision point. CSS cascade layers handle consumption.

css
/* CSS cascade layers for brand selection at consumption */
@layer base, brand;
@import "variables-base.css" layer(base);
@import "variables-brand-a.css" layer(brand);

Review and resolution

  • One alias hop from base is the default. Direct path. No detours.
  • No extra collection to review, audit, or explain to new team members.
  • The three-layer graph keeps review scope small and traceable.
  • CSS @layer and @import handle cascade. Use the platform.
  • Decisions live in files and build inputs, not cramped plugin panels.
  • Any engineer can read the folder structure and understand the brand switch.

Version control and build inputs

The primitives already exist. You do not need new abstraction layers:

  • Files are already versioned. Git tracks every change. History, blame, diff, rollback for free.

  • Paths are already structured. tokens/brand-a/color.json is self-documenting. No second naming system required.

  • Build tools already select files. Style Dictionary's source array. CSS @import. The selector exists.

  • Engineers already know this pattern. Every dev understands folder structure. Not every dev wants to learn design tool panel quirks.

  • One source of truth. The JSON in version control is canonical. The design tool panel is a view, not the source.

Mapped layers are a workaround for tools that cannot do file selection. We have tools that can.

Validate changes before merge

VDS builds on the W3C Design Tokens Community Group specification, adding governance without reinventing the format.

Validation in CI

VDS requires validation before a change merges:

  • JSON schema validation for DTCG compliance
  • Naming convention checks (dot-separated, lowercase)
  • Reference resolution and cycle detection
  • Mode key consistency within collections
  • Breaking change detection with release notes requirements
DTCG types, versioning rules, and deprecation example

DTCG 2025.10 types

json
{
  "border": {
    "default": {
      "$type": "border",
      "$value": {
        "width": { "value": 1, "unit": "px" },
        "color": {
          "colorSpace": "srgb",
          "components": [0.88, 0.88, 0.88],
          "hex": "#e0e0e0"
        },
        "style": "solid"
      }
    }
  },
  "typography": {
    "heading": {
      "$type": "typography",
      "$value": {
        "fontFamily": "{font.family.primary}",
        "fontSize": "{font.size.lg}",
        "fontWeight": "{font.weight.bold}",
        "lineHeight": 1.5
      }
    }
  }
}

Versioning and deprecation

Breaking changes are explicit. Consumers can plan upgrades:

  • MAJOR: Variable renamed or removed, type changed

  • MINOR: New variables, new modes, non-breaking additions

  • PATCH: Value tweaks, documentation updates

json
{
  "color": {
    "primary": {
      "$type": "color",
      "$value": "#0066cc",
      "$deprecated": true,
      "$description": "Use color.brand.primary instead"
    },
    "brand": {
      "primary": {
        "$type": "color",
        "$value": "#0066cc"
      }
    }
  }
}

Where the specification ends

I publish VDS as a living governance specification informed by design systems work. It defines naming, validation, versioning, and adapter patterns on DTCG 2025.10. The intended contract outlives an individual design tool and stays in version control.

VDS is not a new file format, a runtime library, or a design system. It governs variables and their changes; it does not choose a team's design decisions. Validation happens at build time. The three-layer constraint keeps the contract explicit rather than adding another abstraction layer.

The specification describes what a conforming workflow must check. Primitree is where I implement token architecture checks, build output, and change reports. Keeping the specification and the tool separate makes each responsibility reviewable.

Project notes

DTCG

Built on W3C Design Tokens Community Group 2025.10 specification

JSON-as-API

File paths and variable names are the contract interface

3 Layers

Base, Alias, Component. No fourth layer. No extra hops.