Figma Sync Workflow (Figma-WF)
Repeatable workflow for syncing Figma designs to local cache and Xcode assets. Follows PM-WF 1.8 format.
Project Config
| Key | Value |
|---|---|
| Figma file key | tZhdm3G4gxrUrXsTK6iPYh |
| Figma root node | 0:1 |
| Figma MCP server | user-Figma |
| Local cache dir | {PROJECT_ROOT}/../../../figma/ (i.e. Countdown Clock/figma/) |
| Assets target | Chrona/Assets.xcassets/ |
| Notion task DB | 3168d41a5cff80dd813cc84e79466003 |
Workflow Overview
flowchart TD
S0["Stage 0: Check for Changes"] --> NeedSync{"Changes detected?"}
NeedSync -->|"No"| SkipDone["Done - No changes"]
NeedSync -->|"Yes"| S1["Stage 1: Discover Figma Pages"]
S1 --> S2["Stage 2: Export Design Context + Screenshots"]
S2 --> CP1{"CP1: All pages exported?"}
CP1 -->|"Incomplete"| S2
CP1 -->|"Complete"| S3["Stage 3: Export Illustrations/Assets"]
S3 --> S4["Stage 4: Generate Figma-Task Mapping"]
S4 --> S5["Stage 5: Atomic Swap to Local"]
S5 --> CP2{"CP2: Final Verification"}
CP2 -->|"Failed"| S2
CP2 -->|"Passed"| Done["Done - Ready for Dev"]
Incremental Update Strategy
Figma has NO per-node timestamps. Only file-level lastModified exists.
Tier 1 -- File-level gate:
Read figma/sync-state.json. If lastModified unchanged since last sync, skip entirely (zero MCP calls).
Tier 2 -- Node-level metadata hash:
If file changed, call get_metadata on root (1 MCP call). Hash each node's metadata (name, type, position, size, child count). Compare against hashes in sync-state.json. Only re-export nodes whose hash changed.
Limitation: Metadata hash misses pure visual changes (e.g. color swap without structural change). Use --force flag or periodic full sync for those.
MCP Tool Quick Reference
get_metadata
{
"server": "user-Figma",
"toolName": "get_metadata",
"arguments": { "fileKey": "tZhdm3G4gxrUrXsTK6iPYh", "nodeId": "0:1" }
}
Returns: Sparse XML with layer IDs, names, types, positions, sizes. Use to enumerate pages and detect structural changes.
get_design_context
{
"server": "user-Figma",
"toolName": "get_design_context",
"arguments": { "fileKey": "tZhdm3G4gxrUrXsTK6iPYh", "nodeId": "{PAGE_NODE_ID}" }
}
Returns: Code reference (React+Tailwind default), screenshot, design tokens, contextual hints. Primary tool for extracting design details.
get_screenshot
{
"server": "user-Figma",
"toolName": "get_screenshot",
"arguments": { "fileKey": "tZhdm3G4gxrUrXsTK6iPYh", "nodeId": "{NODE_ID}" }
}
Returns: High-res screenshot of the specified node. Use for page-level screenshots and individual asset exports.
Stage 0: Check for Changes
- Read
figma/sync-state.json(create empty state if not found) - Call
get_metadataon root node0:1 - Compare file
lastModifiedagainst stored value - If unchanged: STOP -- print "No changes detected" and exit
- If changed or first sync:
- Parse metadata XML to extract all top-level nodes
- Compute SHA-256 hash of each node's metadata string
- Compare against stored hashes
- Build
changedNodes[]list (or all nodes if first sync)
- If
--forceflag: treat all nodes as changed
Checklist:
- sync-state.json read (or initialized)
- get_metadata called on root
- lastModified compared
- Changed nodes identified
Stage 1: Discover Figma Pages
- From Stage 0 metadata, extract all top-level frames/pages
- Build page index:
| nodeId | Name | Status | Notion Task | |--------|------|--------|-------------| | 0:1 | Home | changed | F1 | | ... | ... | ... | ... | - Map each page to corresponding Notion task (F1-F10) by name matching
- Save index to
figma/.tmp/index.json
Checklist:
- All top-level pages enumerated
- Each page mapped to Notion task
- Status (new/changed/unchanged) assigned
- index.json written to .tmp/
Stage 2: Export Design Context + Screenshots
For each changed page (or all if full sync):
- Call
get_design_contextwith page's nodeId - Save response to
figma/.tmp/pages/{nodeId}/design-context.md - Call
get_screenshotwith page's nodeId - Save screenshot to
figma/.tmp/pages/{nodeId}/screenshot.png - Save raw metadata to
figma/.tmp/pages/{nodeId}/metadata.json
For unchanged pages: copy from existing figma/pages/{nodeId}/ to .tmp/pages/{nodeId}/
Rate limit handling:
- If MCP returns rate limit error, wait 60 seconds and retry
- Track progress in
figma/.tmp/export-progress.json - On retry, skip already-exported nodes
- After 3 consecutive failures, stop and report partial progress
CP1 Checklist:
- Every page in index has a directory in .tmp/pages/
- Each directory contains design-context.md, screenshot.png, metadata.json
- No errors in export-progress.json
Stage 3: Export Illustrations / Custom Assets
- For each changed page, parse
design-context.mdfor illustration/image components - Identify nodes that are custom illustrations (not system icons)
- For each illustration node:
- Call
get_screenshotat 2x scale - Call
get_screenshotat 3x scale - Save to
figma/.tmp/assets/{name}/ - Generate
Contents.jsonfor Xcode imageset:{ "images": [ { "filename": "{name}@2x.png", "idiom": "universal", "scale": "2x" }, { "filename": "{name}@3x.png", "idiom": "universal", "scale": "3x" } ], "info": { "author": "figma-sync", "version": 1 } }
- Call
Checklist:
- All illustration nodes identified
- 2x and 3x PNGs exported
- Contents.json generated per imageset
- Assets ready for Assets.xcassets copy
Stage 4: Generate Figma-Task Mapping
Generate figma/.tmp/FIGMA_MAPPING.md using the template in mapping-template.md.
Three sections:
- Page-to-Task mapping -- Figma page nodeId to Notion task (F1-F10)
- Component-to-Swift mapping -- Figma component to Swift source file
- Design token comparison -- Figma values vs
DesignTokens.swiftvalues
For design token comparison:
- Extract colors, fonts, spacing from
get_design_contextresponses - Read current
Chrona/Design/DesignTokens.swift - Compare and flag mismatches
Checklist:
- All pages mapped to Notion tasks
- Components mapped to Swift files
- Design tokens compared
- Mismatches flagged with specific values
Stage 5: Atomic Swap to Local
Only execute after CP1 passes (all exports successful).
- If
figma/directory exists:- Create backup:
mv figma/ figma/.backup-{YYYYMMDD-HHmmss}/
- Create backup:
- Create fresh
figma/directory - Move from
.tmp/:figma/.tmp/pages/->figma/pages/figma/.tmp/assets/->figma/assets/figma/.tmp/index.json->figma/index.jsonfigma/.tmp/FIGMA_MAPPING.md->figma/FIGMA_MAPPING.md
- Copy each asset imageset to
Chrona/Assets.xcassets/:figma/assets/{name}/->Chrona/Assets.xcassets/{name}.imageset/
- Write updated
figma/sync-state.jsonwith new lastModified and node hashes - Remove
figma/.tmp/directory - Remove backups older than 7 days
Rollback: If any step 2-6 fails, restore from .backup-{timestamp}/
CP2 Final Checklist:
- figma/pages/ has all page directories
- figma/assets/ has all illustration imagesets
- figma/index.json lists all pages
- figma/FIGMA_MAPPING.md is complete
- figma/sync-state.json updated
- Assets.xcassets/ has new imagesets
- .tmp/ directory removed
- No errors during swap
Usage
Full Sync (First Time)
User: "Sync all Figma designs to local"
Agent reads this skill, executes Stage 0-5 with all nodes.
Incremental Sync
User: "Update Figma designs"
Agent reads this skill, executes Stage 0 gate, only re-exports changed nodes.
Force Full Sync
User: "Force full Figma sync"
Agent treats all nodes as changed, bypasses incremental gate.
Single Page Sync
User: "Sync Figma page for Home Screen"
Agent runs Stage 0, then only exports the matching page node.
Additional Resources
- For detailed stage prompts and MCP examples, see reference.md
- For FIGMA_MAPPING.md template, see mapping-template.md
- For sync-state.json schema, see sync-state-schema.json