DesignSystemState Data Structure: Internal Design System Representation in Instagit

The DesignSystemState interface represents a fully-resolved design system in the Instagit CLI, storing tokens, components, and metadata in Map collections for fast lookup after YAML parsing and validation.

The DesignSystemState data structure serves as the central source of truth throughout the Instagit design system pipeline. Defined in the CLI model layer within the google-labs-code/design.md repository, this interface captures all resolved design tokens—including colors, typography, spacing, and component definitions—after the parser and validator have processed the raw YAML. Understanding this internal structure is essential for developers extending the linter, building exporters, or writing test utilities.

Core Properties of DesignSystemState

According to the source code in packages/cli/src/linter/model/spec.ts, the DesignSystemState interface contains several typed properties that organize design data into fast-lookup Map collections.

Token Collections

The primary token categories are stored as Map instances for O(1) access:

  • colors: Map<string, ResolvedColor> — Maps token names to resolved RGBA values with luminance calculations.
  • typography: Map<string, ResolvedTypography> — Maps token names to resolved font families, sizes, weights, and line heights.
  • rounded: Map<string, ResolvedDimension> — Maps token names to border-radius dimensions.
  • spacing: Map<string, ResolvedDimension> — Maps token names to spacing dimensions.

Each Resolved* type conveys the exact shape of the token after model validation, including computed values like WCAG luminance for colors.

Component Definitions

The components property stores a Map<string, ComponentDef> that associates component names with their definitions. Unlike primitive tokens, components maintain their own property maps and track unresolved references.

Symbol Table and Metadata

Auxiliary lookup mechanisms support the design system:

  • symbolTable: Map<string, ResolvedValue> — Provides flat dot-notation lookups (e.g., colors.primary) returning any resolved primitive value.
  • sections: string[] | undefined — Lists Markdown heading names discovered in the source file.
  • unknownKeys and unknownKeyValues: Track unrecognized YAML keys and their raw values for round-tripping purposes.

Optional metadata fields include name and description for human-readable identification.

The ComponentDef Sub-Structure

Defined in the same file (packages/cli/src/linter/model/spec.ts lines 89-94), the ComponentDef interface represents individual component instances within the state:

export interface ComponentDef {
  properties: Map<string, ResolvedValue>;
  /** Unresolved references that failed to resolve */
  unresolvedRefs: string[];
}

The properties map stores component-specific token overrides, while unresolvedRefs captures reference strings that failed to resolve during the validation phase, enabling partial design systems to pass validation while flagging missing dependencies.

Building and Using DesignSystemState

The state is constructed by the model handler in packages/cli/src/linter/model/handler.ts, which transforms the raw ParsedDesignSystem into a fully resolved DesignSystemState after validation.

Manual Construction

For tests or custom scripts, you can instantiate DesignSystemState directly:

import { DesignSystemState } from './linter/model/spec.js';
import { ResolvedColor, ResolvedDimension } from './linter/model/spec.js';

// Helper to build a ResolvedColor
function mkColor(hex: string): ResolvedColor {
  // In practice you would call the real parser; here we mock the fields.
  return {
    type: 'color',
    hex,
    r: parseInt(hex.slice(1, 3), 16),
    g: parseInt(hex.slice(3, 5), 16),
    b: parseInt(hex.slice(5, 7), 16),
    luminance: 0, // omitted for brevity
  };
}

// Minimal state with a single color token
const state: DesignSystemState = {
  name: 'Demo System',
  colors: new Map([['primary', mkColor('#ff0000')]]),
  typography: new Map(),
  rounded: new Map(),
  spacing: new Map(),
  components: new Map(),
  symbolTable: new Map([['colors.primary', mkColor('#ff0000')]]),
};

Test Helper Utilities

The repository provides a buildState helper in packages/cli/src/linter/linter/rules/test-helpers.ts (lines 17-25) that generates a fully resolved state from simple overrides:

import { buildState } from './linter/linter/rules/test-helpers.js';

// Override only the colors you care about
const state = buildState({
  colors: { primary: '#ff0000', accent: '#00ff00' },
  typography: { body: { fontFamily: 'Inter', fontSize: '1rem' } },
});

This utility runs the model handler, resolves references, and returns a ready-to-use DesignSystemState without manual Map construction.

Serialization

To convert the Map-based state into plain JSON for output or storage, use the serializeDesignSystemState function from packages/cli/src/utils.ts (lines 144-151):

import { serializeDesignSystemState } from './utils.js';

const json = serializeDesignSystemState(state);
console.log(JSON.stringify(json, null, 2));

This function recursively converts Map instances into plain objects suitable for JSON emission while preserving the semantic structure of the design system.

Integration with the Model Handler

The DesignSystemState is not created in isolation. According to the implementation in packages/cli/src/linter/model/handler.ts, the model handler processes the ParsedDesignSystem—the raw AST representation—through validation and resolution phases. The handler populates the Map collections, resolves token references, calculates derived values like color luminance, and ultimately produces the immutable state object passed to linters, exporters, and other downstream pipelines.

Summary

  • DesignSystemState is the core interface in packages/cli/src/linter/model/spec.ts representing a fully resolved design system after YAML parsing.
  • Token categories (colors, typography, spacing, rounded) are stored as Map<string, Resolved*> for fast lookup.
  • Components are stored as Map<string, ComponentDef> with their own properties and unresolved reference tracking.
  • The symbolTable enables dot-notation lookups like colors.primary across all token types.
  • Use buildState from test helpers for quick state generation in tests, or serializeDesignSystemState from utils.ts for JSON output.

Frequently Asked Questions

What is the difference between DesignSystemState and ParsedDesignSystem?

ParsedDesignSystem represents the raw AST output immediately after YAML parsing, containing potentially unresolved references and unvalidated values. DesignSystemState represents the fully resolved and validated state after the model handler has processed the parsed data, computed derived values like luminance, and populated the Map collections. The state is the authoritative source passed to downstream tools.

How does the symbolTable property work?

The symbolTable property in DesignSystemState is a flat Map<string, ResolvedValue> that provides dot-notation access to any resolved token in the system. For example, accessing symbolTable.get('colors.primary') returns the same ResolvedColor instance stored in the colors map, enabling unified lookup across all token categories without knowing the specific token type in advance.

Why are token collections stored as Map instead of plain objects?

The DesignSystemState uses Map collections rather than plain objects for token storage to guarantee O(1) lookup performance and preserve insertion order, which is critical for the linter and exporter pipelines processing large design systems. Additionally, Map supports any string key (including those that might conflict with object prototype properties) and provides a cleaner iteration API for the downstream processing logic in the CLI handlers.

Where is DesignSystemState defined in the source code?

The DesignSystemState interface is defined in packages/cli/src/linter/model/spec.ts at lines 71-78, alongside the ComponentDef interface (lines 89-94) and various Resolved* token type definitions. The construction logic resides in packages/cli/src/linter/model/handler.ts, while serialization utilities are found in packages/cli/src/utils.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →