Skip to content

Figma Tokens Plugin(draft)

Simeon Simeonoff edited this page Jan 21, 2026 · 1 revision

Figma Tokens Plugin - Specification v1.0

Related Document: This specification implements the token naming conventions and DTCG format defined in the Design Tokens Specification. Refer to Section 13: Token Naming Conventions and Semantic Standards for authoritative naming rules.

1. Overview

Document Version: 1.0
Plugin Name: Ignite UI Design Tokens Studio Purpose: Export Figma Variables to DTCG format and sync with git repository
Target Audience: Ignite UI Design Team
Development Phases: v1 (Export & Preview), v2 (GitHub Integration)

Last Updated: January 15, 2026
Status: Draft for Review
Author: Simeon Simeonoff


2. Functional Requirements

2.1 Variable Support

Figma Type DTCG Type Support Notes
COLOR color ✅ Full Exported as hex string (#RRGGBB or #RRGGBBAA for alpha)
FLOAT dimension ✅ Full Exported as object: { "value": 8, "unit": "px" }
STRING string ✅ Full
BOOLEAN boolean ✅ Full
VARIABLE_ALIAS Reference {collection.path} ✅ Full Cross-collection refs include collection prefix

Color Value Format:

{
  "$type": "color",
  "$value": "#0066CC"
}

For colors with alpha/opacity:

{
  "$type": "color",
  "$value": "#0066CC80"
}

Dimension Value Format:

{
  "$type": "dimension",
  "$value": {
    "value": 16,
    "unit": "px"
  }
}

Note: All numeric Figma variables (FLOAT) are assumed to represent pixel dimensions unless otherwise specified. Future versions may support unit inference from variable naming context.

2.2 Collection & Mode Handling

  • Scope: Local variables + Library variables
  • Collections: One DTCG file per collection
  • Modes: DTCG-compliant mode handling using $extensions.modes
  • Extended Collections: Export with $extends property (DTCG native)
  • Naming: Use nested object hierarchy matching Figma slash groups

File Output Example:

colors.tokens.json           → "Colors" collection
spacing.tokens.json          → "Spacing" collection  
typography.tokens.json       → "Typography" collection
components.tokens.json       → "Components" collection

Mode Handling Rules:

  1. Default Mode: The collection's defaultModeId determines the token's root $value
  2. Alternate Modes: All non-default modes are added to $extensions.modes, keyed by mode name (lowercase)
  3. Mode Names: Normalize Figma mode names to lowercase (e.g., "Light" → "light", "Dark" → "dark")
  4. Identical Values: If a token has identical values across all modes, omit $extensions.modes

Mode Export Example:

For a token with Light (default) and Dark modes:

{
  "button": {
    "background": {
      "idle": {
        "$type": "color",
        "$value": "{colors.primary.500}",
        "$extensions": {
          "modes": {
            "dark": {
              "$value": "{colors.primary.300}"
            }
          }
        }
      }
    }
  }
}

When the token has the same value in all modes:

{
  "spacing": {
    "md": {
      "$type": "dimension",
      "$value": { "value": 16, "unit": "px" }
    }
  }
}

2.3 Validation Requirements

Pre-export validation is categorized into Errors (block export) and Warnings (export with notification).

2.3.1 Errors (Block Export)

Validation Description
Circular alias references Variable A references B, B references A
Broken references Variable references non-existent variable
Type mismatches Number variable references Color variable
Invalid DTCG characters Variable names contain characters invalid in DTCG
Incorrect casing Variable names not using lowercase kebab-case

2.3.2 Warnings (Export with Notification)

Validation Description
Unknown property name Property term not in standard vocabulary (Section 2.5)
Unknown state name State term not in standard vocabulary
Unknown component part Part name not in standard list
Missing mode values Variable has value in one mode but not another
Empty descriptions Semantic tokens without descriptions
Variant in token path Variant term (e.g., "primary") found in token path

2.3.3 Validation Behavior

  • On Error: Block export, display error list, require fixes before export
  • On Warning: Allow export, display warning summary, log to validation report
  • Validation UI: Show validation status panel with categorized error/warning details
  • Validation Report: Generate validation-report.json alongside token exports

Cross-Reference: Token naming validation rules are defined in the Design Tokens Specification - Section 13

2.4 Metadata Export

Include in DTCG output:

  • ✅ Token descriptions (from Figma)
  • ✅ Figma metadata in $extensions.figma:
    • Variable IDs
    • Collection IDs
    • File name
    • Export timestamp
    • Scopes (if applicable)
  • ✅ Mode values in $extensions.modes (for non-default modes)

Complete Example (with modes, metadata, and correct formats):

{
  "color": {
    "brand": {
      "primary": {
        "$type": "color",
        "$value": "#0066CC",
        "$description": "Primary brand color",
        "$extensions": {
          "modes": {
            "dark": {
              "$value": "#3399FF"
            }
          },
          "figma": {
            "variableId": "VariableID:123:456",
            "collectionId": "VariableCollectionId:789",
            "scopes": ["FRAME_FILL", "SHAPE_FILL"]
          }
        }
      }
    }
  },
  "spacing": {
    "md": {
      "$type": "dimension",
      "$value": { "value": 16, "unit": "px" },
      "$description": "Medium spacing",
      "$extensions": {
        "figma": {
          "variableId": "VariableID:123:789",
          "collectionId": "VariableCollectionId:456"
        }
      }
    }
  },
  "button": {
    "background": {
      "idle": {
        "$type": "color",
        "$value": "{color.brand.primary}",
        "$description": "Default button background",
        "$extensions": {
          "modes": {
            "dark": {
              "$value": "{color.brand.primary}"
            }
          },
          "figma": {
            "variableId": "VariableID:456:123",
            "collectionId": "VariableCollectionId:789"
          }
        }
      }
    }
  }
}

Value Format Summary:

Token Type Format Example
Color (solid) Hex string #RRGGBB "#0066CC"
Color (with alpha) Hex8 string #RRGGBBAA "#0066CC80"
Dimension Object with value/unit { "value": 16, "unit": "px" }
Alias reference DTCG reference syntax "{collection.path.to.token}"
String Plain string "Roboto"
Boolean Boolean literal true / false

2.5 Token Naming Convention Validation

The plugin validates token names against the standard vocabulary defined in the Design Tokens Specification. This ensures consistency between Figma variables and the expected DTCG output.

2.5.1 Token Path Structure

All component tokens must follow this hierarchy:

component[/part]/property[/state]
Segment Required Validation
component Yes Must be lowercase kebab-case
part No Must be from standard parts list (if present)
property Yes Must be from standard properties vocabulary
state No Must be from standard states vocabulary (if present)

2.5.2 Standard Properties Vocabulary

The plugin recognizes these property terms:

Category Valid Terms
Colors background, foreground
Borders border/color, border/width, border/radius, border/style
Shadows shadow
Spacing padding/inline, padding/block, gap
Typography font/size, font/weight, font/family, line-height, letter-spacing
Other opacity, size, min-size, max-size

Non-standard terms trigger warnings:

Non-Standard Suggestion
fill, bg, surface Use background
text, color, text-color Use foreground
outline, stroke Use border/color
corner-radius, roundness Use border/radius
elevation, depth Use shadow

2.5.3 Standard States Vocabulary

Interactive States:

Valid Invalid Alternatives
idle default, rest, normal, base
hover hovered, over, mouseover
pressed active (ambiguous), clicked, pressing
focused focus
disabled inactive, off

Synthetic States:

selected, active, error, success, warning, info, open, checked, unchecked, indeterminate, loading, current, completed

2.5.4 Standard Component Parts

Valid Part Description
(omitted) Root element (implicit)
label Text label element
icon Icon element
prefix, suffix Leading/trailing elements
header, content, footer Section elements
overlay Overlay/backdrop
indicator State indicator
track, thumb Slider/toggle elements
separator Divider element
value, placeholder Input content elements
helper, counter Supporting text elements

2.5.5 Validation Algorithm (Pseudocode)

function validateTokenName(figmaPath):
    segments = figmaPath.split('/')
    
    // Check lowercase kebab-case
    for segment in segments:
        if not isKebabCase(segment):
            return Error("Use lowercase kebab-case")
    
    // Parse structure
    component = segments[0]
    remaining = segments[1:]
    
    // Identify property, part, and state
    (part, property, state) = parseTokenPath(remaining)
    
    // Validate property
    if property not in STANDARD_PROPERTIES:
        suggestion = findSuggestion(property, STANDARD_PROPERTIES)
        return Warning("Unknown property '" + property + "'. Did you mean '" + suggestion + "'?")
    
    // Validate state (if present)
    if state and state not in STANDARD_STATES:
        suggestion = findSuggestion(state, STANDARD_STATES)
        return Warning("Unknown state '" + state + "'. Did you mean '" + suggestion + "'?")
    
    // Validate part (if present)
    if part and part not in STANDARD_PARTS:
        return Warning("Unknown part '" + part + "'. Check standard parts list.")
    
    return Valid()

2.6 Collection Organization Requirements

Figma variable collections should follow this organization for optimal export:

2.6.1 Standard Collections

Collection Name Contents Recommended Modes
Colors Primitive color palette (gray, primary, secondary, etc.) Light, Dark
Typography Font sizes, weights, families, line-heights Default
Spacing Spacing scale values Default
Shadows Shadow definitions Light, Dark
Components Component-specific semantic tokens Light, Dark

2.6.2 Naming Conventions per Collection

Collection Token Path Pattern Example
Colors {category}/{shade} primary/500, gray/100
Typography {property}/{scale} font/size/md, font/weight/bold
Spacing {scale} small, medium, large
Shadows {level} small, medium, large
Components {component}[/{part}]/{property}[/{state}] button/background/idle

2.6.3 Variant Handling

Variants (primary, outlined, ghost) and sizes (small, medium, large) should be handled via:

  1. Separate collections (e.g., Components - Primary, Components - Outlined)
  2. Figma modes on the Components collection

This keeps token paths clean and prevents combinatorial explosion.


3. Phase 1: Export & Preview

3.1 User Interface

Layout:

┌─────────────────────────────────────────────────┐
│  Ignite UI Design Tokens Studio                 │
├──────────────┬──────────────────────────────────┤
│              │                                  │
│  Collection  │  Token Preview                   │
│  List        │  ┌──────────────────────────┐    │
│              │  │ color.brand.primary      │    │
│  ☑ Colors    │  │ ■ #0066CC                │    │
│  ☑ Spacing   │  │ Type: color              │    │
│  ☐ Typography│  │ Mode: Light              │    │
│              │  └──────────────────────────┘    │
│  [Export]    │                                  │
│              │                                  │
├──────────────┴──────────────────────────────────┤
│  JSON Explorer                                  │
│  { ▼ "colors": {                                │
│      ▼ "brand": {                               │
│          "primary": { "$type": "color"...       │
│  [Copy JSON] [Download]                         │
└─────────────────────────────────────────────────┘

Components:

1. Collection List (Left Sidebar):

  • Checkbox selection for export
  • Collection name
  • Token count badge
  • Mode indicator (e.g., "2 modes")
  • Extended collection indicator (shows parent)

2. Token Preview (Main Area):

  • Selected collection details
  • Hierarchical token display
  • Visual previews:
    • Color: Swatch + hex/rgb
    • Number: Value with unit
    • String: Formatted text
    • Boolean: True/False badge
    • Alias: Shows reference chain with → arrows
  • Expandable/collapsible groups

3. JSON Explorer (Bottom Panel):

  • Syntax-highlighted JSON
  • Collapsible tree structure
  • Line numbers
  • Copy button
  • Download button
  • Validation status indicator

4. Toolbar Actions:

  • "Export Selected" - Downloads checked collections
  • "Export All" - Downloads all collections
  • "Settings" - Opens settings panel (placeholder for v2)
  • Validation status indicator

3.2 Export Functionality

Process:

  1. User selects collections
  2. Plugin runs validation:
    • Errors found: Display error panel, block export until resolved
    • Warnings only: Display warning summary, allow export to proceed
    • No issues: Proceed directly to export
  3. User reviews validation summary (if warnings present)
  4. User clicks "Export Selected" or "Export Anyway" (with warnings)
  5. Plugin generates DTCG JSON for each collection
  6. Plugin generates validation-report.json (if any warnings)
  7. Browser downloads files: {collection-name}.tokens.json
  8. Success notification with file count and warning count

Validation Summary UI:

┌──────────────────────────────────────────────────┐
│  ⚠ Validation Summary                            │
├──────────────────────────────────────────────────┤
│  0 Errors   3 Warnings                           │
├──────────────────────────────────────────────────┤
│  Warnings:                                       │
│  ├ button/fill/idle                              │
│  │   Unknown property 'fill'. Use 'background'   │
│  ├ card/bg/hover                                 │
│  │   Unknown property 'bg'. Use 'background'     │
│  └ input/border/hovered                          │
│      Unknown state 'hovered'. Use 'hover'        │
├──────────────────────────────────────────────────┤
│  [Cancel]                    [Export Anyway]     │
└──────────────────────────────────────────────────┘

File Format:

  • JSON with 2-space indentation
  • UTF-8 encoding
  • DTCG 2025.10 compliant

Validation Report Format (validation-report.json):

{
  "timestamp": "2026-01-15T10:30:00Z",
  "collections": ["Colors", "Components"],
  "summary": {
    "errors": 0,
    "warnings": 3
  },
  "issues": [
    {
      "severity": "warning",
      "collection": "Components",
      "token": "button/fill/idle",
      "rule": "unknown-property",
      "message": "Unknown property 'fill'. Did you mean 'background'?"
    }
  ]
}

3.3 Settings Panel (v1 Placeholder)

Basic UI structure for future GitHub config:

  • GitHub repository input (disabled/grayed out)
  • GitHub token input (disabled/grayed out)
  • "Coming soon in v2" message

4. Phase 2: GitHub Integration

4.1 GitHub Authentication

Settings UI:

GitHub Configuration
├─ Repository: [owner/repo-name          ]
├─ Branch:     [main                     ]
├─ Token:      [••••••••••••••••         ] [Test Connection]
├─ Path:       [/tokens                  ] (fixed)
└─ [Save Settings]

Token Storage:

  • Use figma.clientStorage API
  • Per-user, per-file storage
  • Security warning displayed

Validation:

  • Test GitHub API connection on save
  • Verify repo access and write permissions
  • Show connection status

4.2 PR Creation Workflow

UI Flow:

  1. User clicks "Sync to GitHub"
  2. Pre-sync validation:
    • Check GitHub connection
    • Validate all tokens
    • Detect changed collections
  3. PR Configuration Dialog opens:
┌─────────────────────────────────────────┐
│  Create Pull Request                    │
├─────────────────────────────────────────┤
│  Title: [ Update design tokens...  ]    │
│                                         │
│  Description:                           │
│  ┌──────────────────────────────────┐   │
│  │ ## Changes                       │   │
│  │ - Updated: Colors (12 tokens)    │   │
│  │                                  │   │
│  │ Figma file: [link]               │   │
│  └──────────────────────────────────┘   │
│                                         │
│  Reviewers: [Select from list ▼     ]   │
│             □ Simeon Simeonoff          │
│             ☑ Marin Popov               │
│                                         │
│  Labels: design-tokens, figma           │
│                                         │
│  [Cancel]              [Create PR]      │
└─────────────────────────────────────────┘
  1. User edits title/description, selects reviewers
  2. Plugin creates PR:
    • Creates branch: figma-tokens-sync-{timestamp}
    • Commits files to /tokens/
    • Creates PR with config
    • Applies labels: design-tokens, figma, ignite-ui
  3. Success dialog with PR URL

PR Body Auto-Generation:

## Design Token Updates

This PR contains updated design tokens exported from Figma.

### Collections Updated
- **Colors**: 12 tokens modified
- **Spacing**: 8 tokens added

### Figma File
🎨 [View in Figma](figma://file/{fileKey})

### Export Details
- Exported: 2026-01-15 10:30 AM
- Plugin: Ignite UI Design Tokens Exporter v1.0
- Collections: 2

---
*This PR was automatically created by the Ignite UI Design Tokens Exporter plugin.*

4.3 Conflict Handling

Strategy: Create PR with conflicts (let Git handle)

Process:

  • Compare file SHAs with GitHub
  • If files changed: show warning but proceed
  • Git will show conflicts in PR
  • Reviewers handle conflicts during PR review

Warning Message:

⚠️ Token files have been modified in GitHub since last sync. This PR may contain conflicts that need to be resolved.

4.4 Sync Status UI

Add to plugin sidebar:

GitHub Sync Status
├─ Last Sync: 2 hours ago
├─ Last PR:   #123 (Merged) [View]
└─ Status:    ✅ Connected

4.5 Relaunch Buttons

Manifest Configuration:

{
  "relaunchButtons": [
    {
      "command": "export",
      "name": "Export Tokens",
      "multipleSelection": false
    },
    {
      "command": "sync",
      "name": "Sync to GitHub",
      "multipleSelection": false
    }
  ]
}

Behavior:

  • "Export Tokens": Opens plugin to export tab
  • "Sync to GitHub": Opens plugin and starts sync flow
  • Buttons appear in Figma properties panel

5. Technical Architecture

5.1 Project Structure

igniteui-figma-tokens/
├── manifest.json
├── package.json
├── tsconfig.json
├── webpack.config.js
├── src/
│   ├── plugin/
│   │   ├── code.ts                    # Main plugin entry
│   │   ├── api/
│   │   │   ├── figma.ts              # Figma API wrapper
│   │   │   └── github.ts             # GitHub API wrapper
│   │   ├── converters/
│   │   │   ├── figmaToDTCG.ts        # Core conversion logic
│   │   │   ├── colorConverter.ts     # Color transformations
│   │   │   ├── aliasResolver.ts      # Alias handling
│   │   │   └── validator.ts          # DTCG validation
│   │   └── utils/
│   │       ├── naming.ts             # Name transformations
│   │       └── storage.ts            # Settings persistence
│   ├── ui/
│   │   ├── App.svelte                # Main UI component
│   │   ├── components/
│   │   │   ├── CollectionList.svelte
│   │   │   ├── TokenPreview.svelte
│   │   │   ├── JsonExplorer.svelte
│   │   │   ├── ValidationStatus.svelte
│   │   │   ├── SettingsPanel.svelte
│   │   │   └── PRDialog.svelte
│   │   ├── stores/
│   │   │   └── app.ts                # Svelte stores
│   │   └── styles/
│   │       └── global.css
│   ├── types/
│   │   ├── dtcg.ts                   # DTCG type definitions
│   │   ├── figma.d.ts               # Extended Figma types
│   │   └── messages.ts               # UI ↔ Plugin messages
│   └── shared/
│       └── constants.ts              # Shared constants
└── README.md

5.2 Technology Stack

Component Technology Version
Language TypeScript ^5.0
UI Framework Svelte ^5.0
Bundler Vite ^7.0
API Typings @figma/plugin-typings latest
GitHub API Native fetch -

5.3 Key Algorithms

Collection Reading:

async function readCollections() {
  const collections = await figma.variables.getLocalVariableCollectionsAsync()
  
  for (const collection of collections) {
    // Read all variables
    for (const variableId of collection.variableIds) {
      const variable = await figma.variables.getVariableByIdAsync(variableId)
      // Process variable...
    }
    
    // Handle library variables if collection is from library
    if (collection.remote) {
      // Read library variables...
    }
  }
}

DTCG Conversion:

function convertToDTCG(variable: Variable, collection: Collection): DTCGToken {
  const defaultModeId = collection.defaultModeId
  const defaultValue = variable.valuesByMode[defaultModeId]
  
  // Build base token
  const token: DTCGToken = {
    $type: mapFigmaTypeToDTCG(variable.resolvedType),
    $value: convertValue(defaultValue, variable.resolvedType),
    $description: variable.description || undefined,
    $extensions: {
      figma: {
        variableId: variable.id,
        collectionId: collection.id
      }
    }
  }
  
  // Add non-default modes to $extensions.modes
  const modes: Record<string, { $value: any }> = {}
  for (const mode of collection.modes) {
    if (mode.modeId !== defaultModeId) {
      const modeValue = variable.valuesByMode[mode.modeId]
      // Only add if value differs from default
      if (!valuesEqual(modeValue, defaultValue)) {
        modes[mode.name.toLowerCase()] = {
          $value: convertValue(modeValue, variable.resolvedType)
        }
      }
    }
  }
  if (Object.keys(modes).length > 0) {
    token.$extensions.modes = modes
  }
  
  return token
}

function convertValue(value: VariableValue, type: VariableResolvedDataType): any {
  // Handle aliases
  if (isVariableAlias(value)) {
    return formatAliasReference(value.id)
  }
  
  // Handle colors - convert to hex string
  if (type === 'COLOR') {
    return rgbaToHex(value)  // Returns "#RRGGBB" or "#RRGGBBAA"
  }
  
  // Handle dimensions - convert to {value, unit} object
  if (type === 'FLOAT') {
    return { value: value, unit: 'px' }
  }
  
  // Handle strings and booleans directly
  return value
}

function rgbaToHex(color: RGBA): string {
  const r = Math.round(color.r * 255).toString(16).padStart(2, '0')
  const g = Math.round(color.g * 255).toString(16).padStart(2, '0')
  const b = Math.round(color.b * 255).toString(16).padStart(2, '0')
  
  if (color.a < 1) {
    const a = Math.round(color.a * 255).toString(16).padStart(2, '0')
    return `#${r}${g}${b}${a}`.toUpperCase()
  }
  return `#${r}${g}${b}`.toUpperCase()
}

Token Naming Validation:

// Standard vocabularies (from Design Tokens Spec Section 13)
const STANDARD_PROPERTIES = [
  'background', 'foreground',
  'border/color', 'border/width', 'border/radius', 'border/style',
  'shadow', 'padding/inline', 'padding/block', 'gap',
  'font/size', 'font/weight', 'font/family', 'line-height', 'letter-spacing',
  'opacity', 'size', 'min-size', 'max-size'
]

const STANDARD_STATES = [
  // Interactive
  'idle', 'hover', 'pressed', 'focused', 'disabled',
  // Synthetic
  'selected', 'active', 'error', 'success', 'warning', 'info',
  'open', 'checked', 'unchecked', 'indeterminate', 'loading', 'current', 'completed'
]

const STANDARD_PARTS = [
  'label', 'icon', 'prefix', 'suffix', 'header', 'content', 'footer',
  'overlay', 'indicator', 'track', 'thumb', 'separator', 'value',
  'placeholder', 'helper', 'counter'
]

const PROPERTY_SUGGESTIONS: Record<string, string> = {
  'fill': 'background', 'bg': 'background', 'surface': 'background',
  'text': 'foreground', 'color': 'foreground', 'text-color': 'foreground',
  'outline': 'border/color', 'stroke': 'border/color',
  'corner-radius': 'border/radius', 'roundness': 'border/radius',
  'elevation': 'shadow', 'depth': 'shadow'
}

const STATE_SUGGESTIONS: Record<string, string> = {
  'default': 'idle', 'rest': 'idle', 'normal': 'idle', 'base': 'idle',
  'hovered': 'hover', 'over': 'hover', 'mouseover': 'hover',
  'active': 'pressed', 'clicked': 'pressed', 'pressing': 'pressed',
  'focus': 'focused', 'inactive': 'disabled', 'off': 'disabled'
}

interface ValidationResult {
  valid: boolean
  severity: 'error' | 'warning' | null
  message: string | null
  suggestion: string | null
}

function validateTokenName(figmaPath: string): ValidationResult[] {
  const results: ValidationResult[] = []
  const segments = figmaPath.split('/')
  
  // Check lowercase kebab-case
  for (const segment of segments) {
    if (segment !== segment.toLowerCase() || /[A-Z_\s]/.test(segment)) {
      results.push({
        valid: false,
        severity: 'error',
        message: `Segment '${segment}' must be lowercase kebab-case`,
        suggestion: segment.toLowerCase().replace(/[\s_]+/g, '-')
      })
    }
  }
  
  // Parse token structure: component[/part]/property[/state]
  const parsed = parseTokenPath(segments)
  
  // Validate property
  if (parsed.property && !isStandardProperty(parsed.property)) {
    const suggestion = PROPERTY_SUGGESTIONS[parsed.property]
    results.push({
      valid: false,
      severity: 'warning',
      message: `Unknown property '${parsed.property}'`,
      suggestion: suggestion || 'Check standard properties vocabulary'
    })
  }
  
  // Validate state
  if (parsed.state && !STANDARD_STATES.includes(parsed.state)) {
    const suggestion = STATE_SUGGESTIONS[parsed.state]
    results.push({
      valid: false,
      severity: 'warning',
      message: `Unknown state '${parsed.state}'`,
      suggestion: suggestion || 'Check standard states vocabulary'
    })
  }
  
  // Validate part
  if (parsed.part && !STANDARD_PARTS.includes(parsed.part)) {
    results.push({
      valid: false,
      severity: 'warning',
      message: `Unknown part '${parsed.part}'`,
      suggestion: 'Check standard parts list'
    })
  }
  
  return results
}

Alias Resolution:

function resolveAliasPath(variableId: string, variables: Map<string, Variable>): string {
  const variable = variables.get(variableId)
  const nameParts = variable.name.split('/').map(sanitizeName)
  return nameParts.join('.')
}

function formatAliasReference(variableId: string, variables: Map<string, Variable>): string {
  const path = resolveAliasPath(variableId, variables)
  return `{${path}}`
}

function detectCircularReferences(variables: Variable[]): CircularRef[] {
  const visited = new Set()
  const stack = new Set()
  const circular = []
  
  function visit(varId) {
    if (stack.has(varId)) {
      circular.push(buildCircularPath(stack, varId))
      return
    }
    // DFS traversal...
  }
  
  return circular
}

GitHub PR Creation:

async function createPR(config: GitHubConfig, files: DTCGFile[]) {
  // 1. Get base branch SHA
  const baseRef = await fetch(`/repos/${config.repo}/git/ref/heads/${config.branch}`)
  
  // 2. Create new branch
  const branchName = `figma-tokens-sync-${Date.now()}`
  await fetch(`/repos/${config.repo}/git/refs`, {
    method: 'POST',
    body: JSON.stringify({
      ref: `refs/heads/${branchName}`,
      sha: baseRef.sha
    })
  })
  
  // 3. Upload files
  for (const file of files) {
    await fetch(`/repos/${config.repo}/contents/${config.path}/${file.name}`, {
      method: 'PUT',
      body: JSON.stringify({
        message: `Update ${file.name}`,
        content: btoa(file.content),
        branch: branchName
      })
    })
  }
  
  // 4. Create PR
  const pr = await fetch(`/repos/${config.repo}/pulls`, {
    method: 'POST',
    body: JSON.stringify({
      title: config.prTitle,
      body: config.prBody,
      head: branchName,
      base: config.branch,
      labels: ['design-tokens', 'figma', 'ignite-ui']
    })
  })
  
  // 5. Request reviewers
  if (config.reviewers.length > 0) {
    await fetch(`/repos/${config.repo}/pulls/${pr.number}/requested_reviewers`, {
      method: 'POST',
      body: JSON.stringify({ reviewers: config.reviewers })
    })
  }
  
  return pr
}

6. Non-Functional Requirements

6.1 Performance

  • Export < 1000 variables: < 2 seconds
  • Export 1000-5000 variables: < 5 seconds
  • UI should remain responsive during processing
  • Show progress indicator for operations > 1 second

6.2 Error Handling

  • Display user-friendly error messages in plugin UI
  • No console-only errors
  • Specific error types:
    • Validation errors
    • GitHub API errors
    • Network timeouts
    • Permission issues

6.3 Security

  • GitHub tokens stored in clientStorage (encrypted by Figma)
  • Show security warning about token permissions
  • Never log tokens to console
  • Clear tokens on plugin uninstall

6.4 Browser Support

  • Figma Desktop app (Chromium-based)
  • Figma Web app (modern browsers)

7. Acceptance Criteria

Phase 1 (v1):

Core Functionality

  • Reads all local variable collections
  • Reads library variable collections
  • Converts all 4 variable types to DTCG
  • Handles aliases correctly
  • Handles extended collections with $extends
  • Converts Figma hierarchy to nested DTCG object structure
  • Shows visual token previews
  • Shows interactive JSON explorer
  • Exports one file per collection
  • Files are DTCG 2025.10 compliant
  • Includes metadata in $extensions
  • Settings panel placeholder present

Type Conversion

  • Colors exported as hex strings (#RRGGBB or #RRGGBBAA)
  • Dimensions exported as objects with value/unit ({ "value": 16, "unit": "px" })
  • Aliases exported as DTCG reference syntax ({collection.path.to.token})
  • Strings and booleans exported directly

Mode Handling

  • Default mode value goes to token's $value
  • Non-default modes added to $extensions.modes
  • Mode names normalized to lowercase
  • Identical mode values do not create redundant $extensions.modes entries

Validation - Errors (Block Export)

  • Detects circular alias references
  • Detects broken references (non-existent variables)
  • Detects type mismatches in references
  • Detects invalid DTCG characters in names
  • Detects incorrect casing (requires lowercase kebab-case)
  • Displays clear error messages with fix guidance

Validation - Warnings (Allow Export)

  • Warns on unknown property names (suggests standard term)
  • Warns on unknown state names (suggests standard term)
  • Warns on unknown component parts
  • Warns on missing mode values
  • Warns on empty descriptions for semantic tokens
  • Warns on variant terms in token path

Validation UI

  • Shows validation summary before export
  • Categorizes issues as errors vs warnings
  • Allows export with warnings (shows "Export Anyway" button)
  • Blocks export with errors
  • Generates validation-report.json when warnings present

Phase 2 (v2):

  • Stores GitHub config per-user, per-file
  • Connects to GitHub API successfully
  • Creates branches correctly
  • Uploads files to /tokens/ path
  • Creates PRs with editable title/body
  • Applies hardcoded labels
  • Fetches and displays collaborators
  • Requests selected reviewers
  • Handles conflicts (creates PR anyway)
  • Shows sync status
  • Relaunch buttons work correctly

8. Future Enhancements (Post-v2)

Potential features for future versions:

  • Import DTCG back to Figma Variables
  • Batch operations (export multiple files)
  • Token diffing (show what changed)
  • CI/CD integration helpers
  • Webhook notifications
  • Multi-repo support
  • Advanced alias visualization
  • Token usage tracking (where tokens are used in designs)
  • Semantic versioning for token files
  • Token documentation generation

9. Documentation Requirements

README.md must include:

  • Installation instructions
  • Quick start guide
  • Configuration guide (GitHub setup)
  • Troubleshooting section
  • DTCG format explanation
  • Examples

Inline Documentation:

  • JSDoc comments for all public functions
  • Type annotations everywhere
  • Complex algorithms explained

10. Testing Strategy

Unit Tests:

  • Color conversion functions
  • Alias resolution logic
  • Name transformation
  • Circular reference detection
  • DTCG validation

Integration Tests:

  • Full collection export
  • GitHub PR creation flow
  • Settings persistence

Manual Testing Checklist:

  • Export various variable types
  • Handle missing library variables
  • Test with circular references
  • Test GitHub authentication
  • Test PR creation with real repos
  • Test conflict scenarios

11. Design Decisions & Rationale

11.1 Why Svelte?

  • Smaller bundle size compared to React
  • Simpler state management
  • Better performance for plugin UI
  • Easier to learn and maintain

11.2 Why One File Per Collection?

  • Matches Figma's organizational structure
  • Easier for developers to find specific tokens
  • Reduces merge conflicts in version control
  • Aligns with common design system practices

11.3 Why PR-based Sync (Not Direct Commits)?

  • Safer workflow with review process
  • Allows for validation before merging
  • Creates audit trail
  • Enables discussion and collaboration
  • Prevents accidental overwrites

11.4 Why DTCG 2025.10 Full Compliance?

  • Future-proof design token format
  • Industry standard (W3C Community Group)
  • Interoperability with other tools
  • Well-documented specification
  • Community support and adoption

11.5 Why Include Library Variables?

  • Design systems often use shared libraries
  • Complete token export for multi-file projects
  • Enables single source of truth across files
  • Supports complex design system architectures

12. Known Limitations

Phase 1 (v1):

  • Export only (no import from DTCG back to Figma)
  • No real-time preview as variables change
  • No batch export to multiple formats
  • No token diffing between exports

Phase 2 (v2):

  • Single repository support only
  • No automatic sync on variable changes
  • No conflict resolution within plugin
  • No support for GitHub Enterprise Server (only github.com)

DTCG Support:

  • Only core token types (color, number, string, boolean)
  • No composite token types (typography, shadow, etc.)
  • No token transformations or calculations
  • No multi-file token references (only within same collection)

Naming Validation:

  • Naming validation is advisory only (warnings do not block export)
  • Custom property/state/part names are exported as-is with warnings
  • No automatic renaming or correction of non-standard names
  • Validation vocabulary is based on Design Tokens Specification v1.0
  • New vocabulary terms require spec update and plugin update
  • No validation of token value semantics (only structural/naming validation)
  • State combination validation (e.g., error-hover) is limited to format checking

13. Dependencies & Prerequisites

Development Environment:

  • Node.js 18+ and npm
  • Figma Desktop app or web access
  • TypeScript knowledge
  • Svelte familiarity (or willingness to learn)
  • Git and GitHub account

Figma Access:

  • Figma file with Variable Collections
  • Permission to create plugins for development
  • Access to team libraries (if using library variables)

GitHub Access (v2):

  • GitHub account with repository access
  • Personal Access Token with repo scope
  • Write access to target repository

14. Success Metrics

Adoption Metrics:

  • Number of active users
  • Number of exports per week
  • Number of PRs created via plugin

Quality Metrics:

  • Export success rate (> 99%)
  • DTCG validation pass rate
  • GitHub sync success rate (> 95%)
  • Average export time (< 5s for typical use)

User Satisfaction:

  • Time saved vs. manual export
  • Reduction in token-related errors
  • Team feedback and feature requests

15. Support & Maintenance

Bug Reports:

  • GitHub Issues for tracking
  • Clear reproduction steps required
  • Prioritize security and data loss issues

Feature Requests:

  • Community-driven via GitHub Discussions
  • Evaluate against core mission (Ignite UI focus)
  • Consider impact on simplicity/complexity

Updates:

  • Monitor Figma Plugin API changes
  • Track DTCG specification updates
  • Maintain compatibility with GitHub API

16. Glossary

Term Definition
DTCG Design Tokens Community Group - W3C standard
Variable Collection Figma's container for related variables
Mode Figma's theme/variant within a collection (e.g., Light/Dark)
Extended Collection Figma collection that inherits from a parent collection
Alias Reference from one token to another
Library Variable Variable published from a team library
PR Pull Request on GitHub
Token Design decision represented as a name-value pair

Clone this wiki locally