-
Notifications
You must be signed in to change notification settings - Fork 1
Figma Tokens Plugin(draft)
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.
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
| 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.
- Scope: Local variables + Library variables
- Collections: One DTCG file per collection
-
Modes: DTCG-compliant mode handling using
$extensions.modes -
Extended Collections: Export with
$extendsproperty (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:
-
Default Mode: The collection's
defaultModeIddetermines the token's root$value -
Alternate Modes: All non-default modes are added to
$extensions.modes, keyed by mode name (lowercase) - Mode Names: Normalize Figma mode names to lowercase (e.g., "Light" → "light", "Dark" → "dark")
-
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" }
}
}
}Pre-export validation is categorized into Errors (block export) and Warnings (export with notification).
| 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 |
| 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 |
- 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.jsonalongside token exports
Cross-Reference: Token naming validation rules are defined in the Design Tokens Specification - Section 13
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
|
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.
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) |
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
|
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
| 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 |
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()
Figma variable collections should follow this organization for optimal export:
| 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 |
| 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 |
Variants (primary, outlined, ghost) and sizes (small, medium, large) should be handled via:
-
Separate collections (e.g.,
Components - Primary,Components - Outlined) - Figma modes on the Components collection
This keeps token paths clean and prevents combinatorial explosion.
┌─────────────────────────────────────────────────┐
│ 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] │
└─────────────────────────────────────────────────┘
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
Process:
- User selects collections
- 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
- User reviews validation summary (if warnings present)
- User clicks "Export Selected" or "Export Anyway" (with warnings)
- Plugin generates DTCG JSON for each collection
- Plugin generates
validation-report.json(if any warnings) - Browser downloads files:
{collection-name}.tokens.json - 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'?"
}
]
}Basic UI structure for future GitHub config:
- GitHub repository input (disabled/grayed out)
- GitHub token input (disabled/grayed out)
- "Coming soon in v2" message
Settings UI:
GitHub Configuration
├─ Repository: [owner/repo-name ]
├─ Branch: [main ]
├─ Token: [•••••••••••••••• ] [Test Connection]
├─ Path: [/tokens ] (fixed)
└─ [Save Settings]
Token Storage:
- Use
figma.clientStorageAPI - Per-user, per-file storage
- Security warning displayed
Validation:
- Test GitHub API connection on save
- Verify repo access and write permissions
- Show connection status
UI Flow:
- User clicks "Sync to GitHub"
- Pre-sync validation:
- Check GitHub connection
- Validate all tokens
- Detect changed collections
- 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] │
└─────────────────────────────────────────┘
- User edits title/description, selects reviewers
- Plugin creates PR:
- Creates branch:
figma-tokens-sync-{timestamp} - Commits files to
/tokens/ - Creates PR with config
- Applies labels:
design-tokens,figma,ignite-ui
- Creates branch:
- 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.*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.
Add to plugin sidebar:
GitHub Sync Status
├─ Last Sync: 2 hours ago
├─ Last PR: #123 (Merged) [View]
└─ Status: ✅ Connected
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
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
| 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 | - |
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...
}
}
}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()
}// 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
}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
}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
}- Export < 1000 variables: < 2 seconds
- Export 1000-5000 variables: < 5 seconds
- UI should remain responsive during processing
- Show progress indicator for operations > 1 second
- Display user-friendly error messages in plugin UI
- No console-only errors
- Specific error types:
- Validation errors
- GitHub API errors
- Network timeouts
- Permission issues
- GitHub tokens stored in
clientStorage(encrypted by Figma) - Show security warning about token permissions
- Never log tokens to console
- Clear tokens on plugin uninstall
- Figma Desktop app (Chromium-based)
- Figma Web app (modern browsers)
- 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
- Colors exported as hex strings (
#RRGGBBor#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
- 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.modesentries
- 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
- 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
- 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.jsonwhen warnings present
- 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
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
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
- Color conversion functions
- Alias resolution logic
- Name transformation
- Circular reference detection
- DTCG validation
- Full collection export
- GitHub PR creation flow
- Settings persistence
- Export various variable types
- Handle missing library variables
- Test with circular references
- Test GitHub authentication
- Test PR creation with real repos
- Test conflict scenarios
- Smaller bundle size compared to React
- Simpler state management
- Better performance for plugin UI
- Easier to learn and maintain
- Matches Figma's organizational structure
- Easier for developers to find specific tokens
- Reduces merge conflicts in version control
- Aligns with common design system practices
- Safer workflow with review process
- Allows for validation before merging
- Creates audit trail
- Enables discussion and collaboration
- Prevents accidental overwrites
- Future-proof design token format
- Industry standard (W3C Community Group)
- Interoperability with other tools
- Well-documented specification
- Community support and adoption
- 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
- 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
- Single repository support only
- No automatic sync on variable changes
- No conflict resolution within plugin
- No support for GitHub Enterprise Server (only github.com)
- 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 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
- Node.js 18+ and npm
- Figma Desktop app or web access
- TypeScript knowledge
- Svelte familiarity (or willingness to learn)
- Git and GitHub account
- Figma file with Variable Collections
- Permission to create plugins for development
- Access to team libraries (if using library variables)
- GitHub account with repository access
- Personal Access Token with
reposcope - Write access to target repository
- Number of active users
- Number of exports per week
- Number of PRs created via plugin
- Export success rate (> 99%)
- DTCG validation pass rate
- GitHub sync success rate (> 95%)
- Average export time (< 5s for typical use)
- Time saved vs. manual export
- Reduction in token-related errors
- Team feedback and feature requests
- GitHub Issues for tracking
- Clear reproduction steps required
- Prioritize security and data loss issues
- Community-driven via GitHub Discussions
- Evaluate against core mission (Ignite UI focus)
- Consider impact on simplicity/complexity
- Monitor Figma Plugin API changes
- Track DTCG specification updates
- Maintain compatibility with GitHub API
| 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 |