@noemuch/bridge-ds 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/design-workflow/references/actions/design.md +24 -6
- package/skills/design-workflow/references/figma-api-rules.md +59 -13
- package/skills/design-workflow/references/knowledge-base/README.md +13 -4
- package/skills/design-workflow/references/knowledge-base/schemas/assets.md +144 -0
- package/skills/design-workflow/references/knowledge-base/schemas/components.md +124 -0
- package/skills/design-workflow/references/knowledge-base/schemas/text-styles.md +117 -0
- package/skills/design-workflow/references/knowledge-base/schemas/validation.md +182 -0
- package/skills/design-workflow/references/knowledge-base/schemas/variables.md +153 -0
- package/skills/design-workflow/references/onboarding.md +21 -7
- package/skills/design-workflow/references/quality-gates.md +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noemuch/bridge-ds",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "AI-powered design generation in Figma — 100% design system compliant. Connects Claude Code to Figma via MCP for spec-first, token-bound, component-native design.",
|
|
5
5
|
"main": "lib/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -46,6 +46,12 @@ Parse from spec:
|
|
|
46
46
|
|
|
47
47
|
All paths relative to `.claude/skills/design-workflow/references/knowledge-base/`.
|
|
48
48
|
|
|
49
|
+
**Registry key validation (BLOCKING):**
|
|
50
|
+
Before using any registry, verify that entries contain `key` fields (not just `id` or `name`):
|
|
51
|
+
- Components must have `key` (hex hash like `"abc123..."`) — NOT node IDs like `"1008:174"`
|
|
52
|
+
- Variables must have `key` — NOT just name paths like `"color/background/neutral/boldest"`
|
|
53
|
+
- If ANY registry is missing `key` fields → **STOP** and run `schemas/validation.md` procedure before continuing
|
|
54
|
+
|
|
49
55
|
### 1b. Pattern Matching (BLOCKING)
|
|
50
56
|
|
|
51
57
|
**This step is MANDATORY. No design generation without completing it.**
|
|
@@ -109,14 +115,26 @@ Never generate a full design in a single script. Split into small, sequential st
|
|
|
109
115
|
|
|
110
116
|
**CRITICAL — Pre-script element audit (BLOCKING, Rule 18):**
|
|
111
117
|
|
|
112
|
-
Before writing EACH script, list every visual element it will create and
|
|
118
|
+
Before writing EACH script, list every visual element it will create and cross-reference against the spec's "DS Components Used" table AND the registries:
|
|
119
|
+
|
|
113
120
|
```
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
PRE-SCRIPT AUDIT — Step {n}:
|
|
122
|
+
Spec requires: Registry match: Script will use:
|
|
123
|
+
─────────────────────────────────────────────────────────────────────
|
|
124
|
+
AssetLogo (logo) → logos.json key: abc123 → importComponentByKeyAsync ✓
|
|
125
|
+
Tag v2 (label) → components.json key: def456 → importComponentSetByKeyAsync ✓
|
|
126
|
+
CardBase (container) → components.json key: ghi789 → importComponentByKeyAsync ✓
|
|
127
|
+
Section title → NO DS component → raw text (justified) ✓
|
|
128
|
+
Layout wrapper → NO DS component → raw frame (structural) ✓
|
|
118
129
|
```
|
|
119
|
-
|
|
130
|
+
|
|
131
|
+
**BLOCKING RULES:**
|
|
132
|
+
- If a spec-listed DS component is planned as a raw element → **STOP. Rewrite the script.**
|
|
133
|
+
- If an element exists in ANY registry (components, icons, logos, illustrations) → **MUST import it**. No exceptions.
|
|
134
|
+
- If you're about to use `figma.createEllipse()`, `figma.createRectangle()`, or `figma.createFrame()` for something that looks like a DS component → **STOP and check registries first.**
|
|
135
|
+
- Only create raw elements for pure structural layout frames or when NO DS component exists (document WHY in the audit).
|
|
136
|
+
|
|
137
|
+
NEVER recreate as raw frame/text/shape. NEVER hardcode hex colors — always bind variables.
|
|
120
138
|
|
|
121
139
|
**Standard steps for a screen:**
|
|
122
140
|
|
|
@@ -159,10 +159,10 @@ text.fontSize = 32;
|
|
|
159
159
|
|
|
160
160
|
// CORRECT (key from registries/text-styles.json)
|
|
161
161
|
var style = await figma.importStyleByKeyAsync("YOUR_TEXT_STYLE_KEY");
|
|
162
|
-
text.
|
|
162
|
+
await text.setTextStyleIdAsync(style.id); // MUST use async version — see Rule 21
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
**Load text style keys from `registries/text-styles.json`.**
|
|
165
|
+
**Load text style keys from `registries/text-styles.json`.** Always use `setTextStyleIdAsync()`, not `textStyleId =`.
|
|
166
166
|
|
|
167
167
|
---
|
|
168
168
|
|
|
@@ -356,28 +356,38 @@ The `return` before the IIFE is mandatory — without it, the Promise is lost.
|
|
|
356
356
|
|
|
357
357
|
## Rule 18: DS component reuse — NEVER recreate existing components
|
|
358
358
|
|
|
359
|
-
**This is the most critical rule. Violations require script rewrite.**
|
|
359
|
+
**This is the most critical rule. Violations require FULL script rewrite.**
|
|
360
360
|
|
|
361
361
|
Before creating ANY visual element, check the registries:
|
|
362
|
-
1. `registries/components.json` → Buttons, Tags, Inputs, Avatars, Dividers, etc.
|
|
362
|
+
1. `registries/components.json` → Buttons, Tags, Inputs, Avatars, Dividers, Cards, etc.
|
|
363
363
|
2. `registries/icons.json` → Icons (if exists)
|
|
364
|
-
3. `registries/logos.json` → Logos (if exists)
|
|
364
|
+
3. `registries/logos.json` → Logos, brand assets (if exists)
|
|
365
365
|
4. `registries/illustrations.json` → Illustrations (if exists)
|
|
366
366
|
|
|
367
367
|
**NEVER:**
|
|
368
|
-
-
|
|
369
|
-
-
|
|
370
|
-
-
|
|
368
|
+
- `figma.createEllipse()` for a logo → import from logos.json via `importComponentByKeyAsync`
|
|
369
|
+
- `figma.createFrame()` for a Tag/Badge → import from components.json via `importComponentSetByKeyAsync`
|
|
370
|
+
- `figma.createFrame()` for a Card → import the DS Card component
|
|
371
|
+
- `figma.createRectangle()` for a Divider → import the DS Divider component
|
|
371
372
|
- Hardcode hex colors → always bind variables
|
|
372
373
|
|
|
373
|
-
**
|
|
374
|
+
**Why this matters:** Using raw elements cascades into multiple failures:
|
|
375
|
+
- No `INSTANCE_SWAP` props possible (can't swap a logo on an ellipse)
|
|
376
|
+
- No inherited component properties (size, variant, state)
|
|
377
|
+
- Wrong proportions (raw frames don't match DS component sizing)
|
|
378
|
+
- Breaks the design system contract — the whole point of Bridge
|
|
379
|
+
|
|
380
|
+
**Pre-script audit (MANDATORY — must appear before every script):**
|
|
374
381
|
```
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
382
|
+
PRE-SCRIPT AUDIT — Step {n}:
|
|
383
|
+
Spec requires: Registry match: Script will use:
|
|
384
|
+
─────────────────────────────────────────────────────────────────────
|
|
385
|
+
{element} → {registry}.json key: {key} → import{method} ✓
|
|
386
|
+
{element} → NO DS component → raw {type} (reason) ✓
|
|
379
387
|
```
|
|
380
388
|
|
|
389
|
+
**If ANY spec-listed DS component is planned as a raw element → STOP and fix before writing the script.**
|
|
390
|
+
|
|
381
391
|
---
|
|
382
392
|
|
|
383
393
|
## Rule 19: Canvas positioning — never stack components
|
|
@@ -400,6 +410,42 @@ mainComponent.y = 400;
|
|
|
400
410
|
|
|
401
411
|
---
|
|
402
412
|
|
|
413
|
+
## Rule 20: Component key vs Node ID (CRITICAL)
|
|
414
|
+
|
|
415
|
+
Figma components have TWO different identifiers:
|
|
416
|
+
|
|
417
|
+
| | Node ID (`id`) | Component Key (`key`) |
|
|
418
|
+
|---|---|---|
|
|
419
|
+
| **Format** | `"1008:174"` (colon-separated numbers) | `"abc123def456..."` (hex hash) |
|
|
420
|
+
| **Used for** | `figma.getNodeByIdAsync()` | `importComponentByKeyAsync()` |
|
|
421
|
+
| **Available from** | Any node | Only published components/variables/styles |
|
|
422
|
+
|
|
423
|
+
**These are NOT interchangeable.** Import APIs (`importComponentByKeyAsync`, `importComponentSetByKeyAsync`, `importVariableByKeyAsync`, `importStyleByKeyAsync`) all require the `key`, NOT the `id`.
|
|
424
|
+
|
|
425
|
+
Similarly, variables have:
|
|
426
|
+
- `name`: `"color/background/neutral/boldest"` — human-readable path
|
|
427
|
+
- `key`: `"VariableID:55:114"` or hex hash — for `importVariableByKeyAsync`
|
|
428
|
+
|
|
429
|
+
**Always verify registries contain `key` fields before writing import scripts. If a registry only has `id` or `name` values, re-run extraction to capture keys.**
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Rule 21: setTextStyleIdAsync (not textStyleId) in dynamic-page context
|
|
434
|
+
|
|
435
|
+
When running scripts via `figma_execute` (which uses `documentAccess: "dynamic-page"`), text style assignment MUST use the async API:
|
|
436
|
+
|
|
437
|
+
```js
|
|
438
|
+
// WRONG — crashes in dynamic-page context
|
|
439
|
+
text.textStyleId = style.id;
|
|
440
|
+
|
|
441
|
+
// CORRECT
|
|
442
|
+
await text.setTextStyleIdAsync(style.id);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Same applies to `setFillStyleIdAsync`, `setStrokeStyleIdAsync`, `setEffectStyleIdAsync`.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
403
449
|
## Standard Script Boilerplate
|
|
404
450
|
|
|
405
451
|
```js
|
|
@@ -6,6 +6,13 @@ This directory contains your design system's complete documentation, generated b
|
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
knowledge-base/
|
|
9
|
+
schemas/ ← Registry format definitions (read during setup)
|
|
10
|
+
components.md ← components.json schema (required fields, extraction scripts)
|
|
11
|
+
variables.md ← variables.json schema
|
|
12
|
+
text-styles.md ← text-styles.json schema
|
|
13
|
+
assets.md ← icons/logos/illustrations schema
|
|
14
|
+
validation.md ← Post-extraction validation procedure
|
|
15
|
+
|
|
9
16
|
registries/ ← Raw DS data (auto-extracted from Figma)
|
|
10
17
|
components.json ← Component names, keys, variants, properties
|
|
11
18
|
variables.json ← Variable names, keys, types, values by mode
|
|
@@ -42,10 +49,12 @@ knowledge-base/
|
|
|
42
49
|
|
|
43
50
|
Run `/design-workflow setup` in Claude Code. Claude will:
|
|
44
51
|
|
|
45
|
-
1. **Extract** your DS from Figma via
|
|
46
|
-
2. **
|
|
47
|
-
3. **
|
|
48
|
-
4. **
|
|
52
|
+
1. **Extract** your DS from Figma via MCP tools
|
|
53
|
+
2. **Write registries** following the schemas in `schemas/` (every entry must have a `key` field)
|
|
54
|
+
3. **Validate** registries by test-importing sample keys via `figma_execute`
|
|
55
|
+
4. **Analyze** the raw data and write intelligent guides
|
|
56
|
+
5. **Ask for screenshots** of your product's key screens
|
|
57
|
+
6. **Generate** layout pattern documentation from the screenshots
|
|
49
58
|
|
|
50
59
|
## How to update
|
|
51
60
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Schema: icons.json, logos.json, illustrations.json
|
|
2
|
+
|
|
3
|
+
> **Read this BEFORE writing asset registries during setup.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Required Structure (same for all 3 files)
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"meta": {
|
|
12
|
+
"source": "Library file name",
|
|
13
|
+
"fileKey": "FigmaFileKey",
|
|
14
|
+
"extractedAt": "YYYY-MM-DD",
|
|
15
|
+
"totalComponents": 344
|
|
16
|
+
},
|
|
17
|
+
"items": [
|
|
18
|
+
{
|
|
19
|
+
"name": "icon/utility/check",
|
|
20
|
+
"key": "abc123def456...",
|
|
21
|
+
"type": "COMPONENT"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "icon/utility/chevron-up",
|
|
25
|
+
"key": "def789ghi012...",
|
|
26
|
+
"type": "COMPONENT"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"categories": {
|
|
30
|
+
"utility": {
|
|
31
|
+
"description": "UI action and navigation icons",
|
|
32
|
+
"count": 280,
|
|
33
|
+
"namingPattern": "icon/utility/{name}"
|
|
34
|
+
},
|
|
35
|
+
"flag": {
|
|
36
|
+
"description": "Country flag icons",
|
|
37
|
+
"count": 40,
|
|
38
|
+
"namingPattern": "icon/flag/{country}"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Field Requirements
|
|
47
|
+
|
|
48
|
+
### Per asset entry (`items` array)
|
|
49
|
+
|
|
50
|
+
| Field | Required | Description |
|
|
51
|
+
|-------|----------|-------------|
|
|
52
|
+
| `name` | **YES** | Component name/path |
|
|
53
|
+
| `key` | **YES** | Component key for `importComponentByKeyAsync`. NOT a node ID. |
|
|
54
|
+
| `type` | **YES** | `"COMPONENT"` or `"COMPONENT_SET"` |
|
|
55
|
+
|
|
56
|
+
### Categories (documentation only)
|
|
57
|
+
|
|
58
|
+
Categories provide human-readable organization and naming patterns. They help Claude find the right asset without scanning all items.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Large Libraries (500+ items)
|
|
63
|
+
|
|
64
|
+
For very large asset libraries (e.g., 1300+ financial logos), extracting every key upfront is impractical:
|
|
65
|
+
|
|
66
|
+
1. **Extract keys for commonly-used items** (top 50-100) into the `items` array
|
|
67
|
+
2. **Document naming patterns** in `categories` for the rest
|
|
68
|
+
3. **On-demand extraction**: When a specific asset is needed during `design`, use `figma_execute` to search by name and get the key:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
return (async function() {
|
|
72
|
+
var node = figma.currentPage.findOne(function(n) {
|
|
73
|
+
return n.name === "TARGET_NAME" && (n.type === "COMPONENT" || n.type === "COMPONENT_SET");
|
|
74
|
+
});
|
|
75
|
+
if (node) {
|
|
76
|
+
return { name: node.name, key: node.key, type: node.type };
|
|
77
|
+
}
|
|
78
|
+
return { error: "Not found" };
|
|
79
|
+
})();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## How to Extract Keys
|
|
85
|
+
|
|
86
|
+
### Via figma_execute
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
return (async function() {
|
|
90
|
+
var results = [];
|
|
91
|
+
// Get all components on the current page
|
|
92
|
+
var nodes = figma.currentPage.findAll(function(n) {
|
|
93
|
+
return n.type === "COMPONENT" || n.type === "COMPONENT_SET";
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
97
|
+
var n = nodes[i];
|
|
98
|
+
// Skip individual variants inside component sets
|
|
99
|
+
if (n.type === "COMPONENT" && n.parent && n.parent.type === "COMPONENT_SET") continue;
|
|
100
|
+
|
|
101
|
+
results.push({
|
|
102
|
+
name: n.name,
|
|
103
|
+
key: n.key, // ← REQUIRED for importComponentByKeyAsync
|
|
104
|
+
type: n.type
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { items: results, total: results.length };
|
|
109
|
+
})();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Run on each page of the asset library file. For multi-page libraries, run once per page.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Usage in Scripts
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
// Import icon by key (from registries/icons.json)
|
|
120
|
+
var checkIcon = await figma.importComponentByKeyAsync("abc123def456...");
|
|
121
|
+
var iconInstance = checkIcon.createInstance();
|
|
122
|
+
|
|
123
|
+
// Import logo with variants by key
|
|
124
|
+
var logo = await figma.importComponentSetByKeyAsync("xyz789...");
|
|
125
|
+
var defaultVariant = logo.defaultVariant;
|
|
126
|
+
var logoInstance = defaultVariant.createInstance();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## File-specific Notes
|
|
132
|
+
|
|
133
|
+
### icons.json
|
|
134
|
+
- Icons are typically single components (`type: "COMPONENT"`)
|
|
135
|
+
- Naming convention: `icon/{category}/{name}` (e.g., `icon/utility/check`)
|
|
136
|
+
|
|
137
|
+
### logos.json
|
|
138
|
+
- Logos may be component sets with mode variants (dark/light)
|
|
139
|
+
- Naming convention: `{brand}` or `logo/{brand}/{variant}`
|
|
140
|
+
|
|
141
|
+
### illustrations.json
|
|
142
|
+
- Illustrations often have theme variants (dark/light/brand)
|
|
143
|
+
- May be component sets (`type: "COMPONENT_SET"`)
|
|
144
|
+
- Naming convention: `{category}/{name}` (e.g., `asset/crypto`, `utility/empty-state`)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Schema: components.json
|
|
2
|
+
|
|
3
|
+
> **Read this BEFORE writing components.json during setup.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Required Structure
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"meta": {
|
|
12
|
+
"source": "Library file name",
|
|
13
|
+
"fileKey": "FigmaFileKey",
|
|
14
|
+
"extractedAt": "YYYY-MM-DD",
|
|
15
|
+
"totalComponents": 100,
|
|
16
|
+
"totalComponentSets": 76
|
|
17
|
+
},
|
|
18
|
+
"components": {
|
|
19
|
+
"categoryName": [
|
|
20
|
+
{
|
|
21
|
+
"name": "Button",
|
|
22
|
+
"key": "abc123def456789...",
|
|
23
|
+
"id": "1008:174",
|
|
24
|
+
"type": "COMPONENT_SET",
|
|
25
|
+
"variants": 150,
|
|
26
|
+
"properties": {
|
|
27
|
+
"label": "TEXT",
|
|
28
|
+
"hasTrailingIcon": "BOOLEAN",
|
|
29
|
+
"leadingIcon": "INSTANCE_SWAP",
|
|
30
|
+
"variant": "VARIANT(primary,secondary,ghost)",
|
|
31
|
+
"state": "VARIANT(default,hover,disabled)",
|
|
32
|
+
"size": "VARIANT(large,medium,small)"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Field Requirements
|
|
43
|
+
|
|
44
|
+
| Field | Required | Description |
|
|
45
|
+
|-------|----------|-------------|
|
|
46
|
+
| `name` | **YES** | Human-readable component name |
|
|
47
|
+
| `key` | **YES** | Component key for `importComponentByKeyAsync` / `importComponentSetByKeyAsync`. A hex hash like `"abc123def456..."` (NOT a node ID). |
|
|
48
|
+
| `id` | optional | Figma node ID like `"1008:174"`. Useful for lookups but NOT for imports. |
|
|
49
|
+
| `type` | **YES** | `"COMPONENT"` or `"COMPONENT_SET"`. Determines which import API to use. |
|
|
50
|
+
| `variants` | optional | Number of variants (only for COMPONENT_SET) |
|
|
51
|
+
| `properties` | **YES** | Object mapping property names to types: `TEXT`, `BOOLEAN`, `INSTANCE_SWAP`, `VARIANT(...)` |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## CRITICAL: `key` vs `id`
|
|
56
|
+
|
|
57
|
+
These are TWO DIFFERENT identifiers in Figma:
|
|
58
|
+
|
|
59
|
+
| | `id` (node ID) | `key` (component key) |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| **Looks like** | `"1008:174"` | `"abc123def456789abcdef..."` |
|
|
62
|
+
| **Used for** | `figma.getNodeById()` | `importComponentByKeyAsync()` |
|
|
63
|
+
| **Available from** | Any node | Only published components |
|
|
64
|
+
|
|
65
|
+
**`importComponentByKeyAsync` REQUIRES the `key`, NOT the `id`.** Using a node ID as a key will crash the script silently.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## How to Extract Keys
|
|
70
|
+
|
|
71
|
+
### Option A: Via figma_execute (RECOMMENDED)
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
return (async function() {
|
|
75
|
+
var results = [];
|
|
76
|
+
var nodes = figma.currentPage.findAll(function(n) {
|
|
77
|
+
return n.type === "COMPONENT_SET" || (n.type === "COMPONENT" && !n.parent.type !== "COMPONENT_SET");
|
|
78
|
+
});
|
|
79
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
80
|
+
var n = nodes[i];
|
|
81
|
+
results.push({
|
|
82
|
+
name: n.name,
|
|
83
|
+
key: n.key, // ← THIS is what importComponentByKeyAsync needs
|
|
84
|
+
id: n.id, // ← This is just the node ID (different!)
|
|
85
|
+
type: n.type,
|
|
86
|
+
variants: n.type === "COMPONENT_SET" ? n.children.length : undefined,
|
|
87
|
+
properties: n.type === "COMPONENT_SET" ? n.componentPropertyDefinitions : undefined
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return { components: results };
|
|
91
|
+
})();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run this script on each page of the DS library file.
|
|
95
|
+
|
|
96
|
+
### Option B: Via figma_get_design_system_kit
|
|
97
|
+
|
|
98
|
+
If the MCP tool returns component data, look for the `key` field in the response. If only `id` is returned, use Option A to get the actual keys.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Import API Decision
|
|
103
|
+
|
|
104
|
+
| `type` value | Import API |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `COMPONENT_SET` | `figma.importComponentSetByKeyAsync(key)` |
|
|
107
|
+
| `COMPONENT` | `figma.importComponentByKeyAsync(key)` |
|
|
108
|
+
|
|
109
|
+
Check the `type` field to decide which API to use. Using the wrong one will fail.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Category Grouping
|
|
114
|
+
|
|
115
|
+
Group components by semantic category:
|
|
116
|
+
- `actions` — Buttons, IconButtons, Links
|
|
117
|
+
- `formControls` — Inputs, Selects, Checkboxes, Radios, Toggles
|
|
118
|
+
- `dataDisplay` — Tables, Lists, Cards, Badges, Tags
|
|
119
|
+
- `feedback` — Alerts, Toasts, Modals, Dialogs
|
|
120
|
+
- `navigation` — Tabs, Sidebar, Breadcrumbs, Pagination
|
|
121
|
+
- `layout` — Dividers, Spacers, Containers
|
|
122
|
+
- `indicators` — Avatars, Status, Progress
|
|
123
|
+
|
|
124
|
+
Use whatever categories match the DS's own organization.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Schema: text-styles.json
|
|
2
|
+
|
|
3
|
+
> **Read this BEFORE writing text-styles.json during setup.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Required Structure
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"meta": {
|
|
12
|
+
"source": "Library file name",
|
|
13
|
+
"fileKey": "FigmaFileKey",
|
|
14
|
+
"extractedAt": "YYYY-MM-DD",
|
|
15
|
+
"totalStyles": 56
|
|
16
|
+
},
|
|
17
|
+
"textStyles": {
|
|
18
|
+
"display": [
|
|
19
|
+
{ "name": "display/lg", "key": "a9b866d18e77632daa1f4056b905af688933c4c7", "nodeId": "60:169" }
|
|
20
|
+
],
|
|
21
|
+
"heading": [
|
|
22
|
+
{ "name": "heading/xl/regular", "key": "930df26f44235299875c572ef52bd60cbf5ae956", "nodeId": "60:172" },
|
|
23
|
+
{ "name": "heading/xl/accent", "key": "225299706052d97c6259e70545f448cb44e84f9f", "nodeId": "60:173" }
|
|
24
|
+
],
|
|
25
|
+
"body": [
|
|
26
|
+
{ "name": "body/lg/regular", "key": "fe5e50c7cbdc2b607aecb70747cfae7ac4da49ee", "nodeId": "60:206" }
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"effectStyles": {
|
|
30
|
+
"shadow": [
|
|
31
|
+
{ "name": "shadow/xsmall", "key": "1b34455796052a6ec745a03064597491bb8db256", "nodeId": "2620:254" }
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"sizeMap": {
|
|
35
|
+
"display/lg": { "fontSize": 40, "lineHeight": 88, "fontFamily": "Inter" },
|
|
36
|
+
"heading/xl": { "fontSize": 40, "lineHeight": 48, "fontFamily": "Inter" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Field Requirements
|
|
44
|
+
|
|
45
|
+
### Per text style entry
|
|
46
|
+
|
|
47
|
+
| Field | Required | Description |
|
|
48
|
+
|-------|----------|-------------|
|
|
49
|
+
| `name` | **YES** | Style name like `"heading/xl/regular"` |
|
|
50
|
+
| `key` | **YES** | Style key (40-char hex hash) for `importStyleByKeyAsync` |
|
|
51
|
+
| `nodeId` | optional | Figma node ID for reference |
|
|
52
|
+
|
|
53
|
+
### sizeMap (optional but recommended)
|
|
54
|
+
|
|
55
|
+
Maps style names to font specs for quick human reference. Not used in scripts (scripts use the `key` to import the full style).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## How to Extract Keys
|
|
60
|
+
|
|
61
|
+
### Via figma_execute
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
return (async function() {
|
|
65
|
+
var textStyles = await figma.getLocalTextStylesAsync();
|
|
66
|
+
var effectStyles = await figma.getLocalEffectStylesAsync();
|
|
67
|
+
|
|
68
|
+
var result = { textStyles: [], effectStyles: [] };
|
|
69
|
+
|
|
70
|
+
for (var i = 0; i < textStyles.length; i++) {
|
|
71
|
+
var s = textStyles[i];
|
|
72
|
+
result.textStyles.push({
|
|
73
|
+
name: s.name,
|
|
74
|
+
key: s.key, // ← 40-char hex hash for importStyleByKeyAsync
|
|
75
|
+
nodeId: s.id,
|
|
76
|
+
fontSize: s.fontSize,
|
|
77
|
+
lineHeight: s.lineHeight,
|
|
78
|
+
fontFamily: s.fontName.family,
|
|
79
|
+
fontStyle: s.fontName.style
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (var i = 0; i < effectStyles.length; i++) {
|
|
84
|
+
var s = effectStyles[i];
|
|
85
|
+
result.effectStyles.push({
|
|
86
|
+
name: s.name,
|
|
87
|
+
key: s.key,
|
|
88
|
+
nodeId: s.id
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result;
|
|
93
|
+
})();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Usage in Scripts
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
// Import text style by key (from registries/text-styles.json)
|
|
102
|
+
var headingStyle = await figma.importStyleByKeyAsync("930df26f44235299875c572ef52bd60cbf5ae956");
|
|
103
|
+
await textNode.setTextStyleIdAsync(headingStyle.id); // MUST use async version (Rule 20)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Grouping
|
|
109
|
+
|
|
110
|
+
Group text styles by their first path segment:
|
|
111
|
+
- `display` — Large hero/display text
|
|
112
|
+
- `heading` — Section and page headings
|
|
113
|
+
- `body` — Body copy, descriptions
|
|
114
|
+
- `caption` — Small labels, metadata
|
|
115
|
+
- `code` — Monospace / code text
|
|
116
|
+
|
|
117
|
+
Weight/emphasis variants are the second segment: `regular`, `accent`, `emphasis`, `bold`.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Registry Validation Procedure
|
|
2
|
+
|
|
3
|
+
> **Run this AFTER writing all registries and BEFORE generating guides.**
|
|
4
|
+
> This step is BLOCKING — do not proceed until all validations pass.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Phase 1 — Structural Check
|
|
9
|
+
|
|
10
|
+
Read each registry file and verify:
|
|
11
|
+
|
|
12
|
+
### components.json
|
|
13
|
+
- [ ] Every entry has a `key` field (hex hash, NOT a node ID with `:` separator)
|
|
14
|
+
- [ ] Every entry has a `type` field (`"COMPONENT"` or `"COMPONENT_SET"`)
|
|
15
|
+
- [ ] Every entry has a `properties` object
|
|
16
|
+
- [ ] `key` values look like hex hashes (long alphanumeric strings), NOT like `"1008:174"`
|
|
17
|
+
|
|
18
|
+
### variables.json
|
|
19
|
+
- [ ] Every variable entry has a `key` field
|
|
20
|
+
- [ ] Every variable entry has a `name` field (human-readable path)
|
|
21
|
+
- [ ] Every variable entry has `valuesByMode`
|
|
22
|
+
- [ ] `key` values are NOT the same as `name` values (names look like `color/background/...`, keys look like `VariableID:...` or hex hashes)
|
|
23
|
+
|
|
24
|
+
### text-styles.json
|
|
25
|
+
- [ ] Every text style entry has a `key` field (40-char hex hash)
|
|
26
|
+
- [ ] Every entry has a `name` field
|
|
27
|
+
|
|
28
|
+
### icons.json / logos.json / illustrations.json (if present)
|
|
29
|
+
- [ ] Every entry in `items` array has a `key` field
|
|
30
|
+
- [ ] Every entry has a `type` field
|
|
31
|
+
|
|
32
|
+
**If ANY check fails:** Re-extract the failing registry using the extraction scripts in `schemas/components.md`, `schemas/variables.md`, etc.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Phase 2 — Import Test
|
|
37
|
+
|
|
38
|
+
Run a validation script via `figma_execute` that test-imports a sample of keys from each registry.
|
|
39
|
+
|
|
40
|
+
### Validation Script Template
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
return (async function() {
|
|
44
|
+
var results = [];
|
|
45
|
+
|
|
46
|
+
// ── TEST COMPONENT KEYS ──
|
|
47
|
+
// Pick 3-5 component keys from registries/components.json
|
|
48
|
+
var componentTests = [
|
|
49
|
+
{ name: "COMPONENT_NAME_1", key: "KEY_1", type: "COMPONENT_SET" },
|
|
50
|
+
{ name: "COMPONENT_NAME_2", key: "KEY_2", type: "COMPONENT" },
|
|
51
|
+
{ name: "COMPONENT_NAME_3", key: "KEY_3", type: "COMPONENT_SET" }
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (var i = 0; i < componentTests.length; i++) {
|
|
55
|
+
var t = componentTests[i];
|
|
56
|
+
try {
|
|
57
|
+
if (t.type === "COMPONENT_SET") {
|
|
58
|
+
await figma.importComponentSetByKeyAsync(t.key);
|
|
59
|
+
} else {
|
|
60
|
+
await figma.importComponentByKeyAsync(t.key);
|
|
61
|
+
}
|
|
62
|
+
results.push({ type: "component", name: t.name, status: "OK" });
|
|
63
|
+
} catch(e) {
|
|
64
|
+
results.push({ type: "component", name: t.name, status: "FAIL", error: e.message });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── TEST VARIABLE KEYS ──
|
|
69
|
+
// Pick 3-5 variable keys from registries/variables.json (mix of color + spacing)
|
|
70
|
+
var variableTests = [
|
|
71
|
+
{ name: "VARIABLE_NAME_1", key: "KEY_1" },
|
|
72
|
+
{ name: "VARIABLE_NAME_2", key: "KEY_2" },
|
|
73
|
+
{ name: "VARIABLE_NAME_3", key: "KEY_3" }
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
for (var i = 0; i < variableTests.length; i++) {
|
|
77
|
+
var t = variableTests[i];
|
|
78
|
+
try {
|
|
79
|
+
await figma.variables.importVariableByKeyAsync(t.key);
|
|
80
|
+
results.push({ type: "variable", name: t.name, status: "OK" });
|
|
81
|
+
} catch(e) {
|
|
82
|
+
results.push({ type: "variable", name: t.name, status: "FAIL", error: e.message });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── TEST TEXT STYLE KEYS ──
|
|
87
|
+
// Pick 2-3 text style keys from registries/text-styles.json
|
|
88
|
+
var styleTests = [
|
|
89
|
+
{ name: "STYLE_NAME_1", key: "KEY_1" },
|
|
90
|
+
{ name: "STYLE_NAME_2", key: "KEY_2" }
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
for (var i = 0; i < styleTests.length; i++) {
|
|
94
|
+
var t = styleTests[i];
|
|
95
|
+
try {
|
|
96
|
+
await figma.importStyleByKeyAsync(t.key);
|
|
97
|
+
results.push({ type: "textStyle", name: t.name, status: "OK" });
|
|
98
|
+
} catch(e) {
|
|
99
|
+
results.push({ type: "textStyle", name: t.name, status: "FAIL", error: e.message });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── SUMMARY ──
|
|
104
|
+
var passed = results.filter(function(r) { return r.status === "OK"; }).length;
|
|
105
|
+
var failed = results.filter(function(r) { return r.status === "FAIL"; }).length;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
total: results.length,
|
|
109
|
+
passed: passed,
|
|
110
|
+
failed: failed,
|
|
111
|
+
results: results
|
|
112
|
+
};
|
|
113
|
+
})();
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### How to Use
|
|
117
|
+
|
|
118
|
+
1. Read the registries you just wrote
|
|
119
|
+
2. Pick sample keys from each (prioritize commonly-used components like Button, Input, Tag + core variables like spacing/medium, background colors + main text styles)
|
|
120
|
+
3. Replace the placeholder names and keys in the script above
|
|
121
|
+
4. Run via `figma_execute`
|
|
122
|
+
5. Check results
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Phase 3 — Remediation
|
|
127
|
+
|
|
128
|
+
If any keys fail validation:
|
|
129
|
+
|
|
130
|
+
### Component key failed
|
|
131
|
+
→ The key is likely a node ID instead of a component key. Re-extract using:
|
|
132
|
+
```js
|
|
133
|
+
return (async function() {
|
|
134
|
+
var node = await figma.getNodeByIdAsync("NODE_ID_FROM_REGISTRY");
|
|
135
|
+
if (node) {
|
|
136
|
+
return { name: node.name, correctKey: node.key, incorrectId: node.id };
|
|
137
|
+
}
|
|
138
|
+
return { error: "Node not found" };
|
|
139
|
+
})();
|
|
140
|
+
```
|
|
141
|
+
Update the registry entry with the `correctKey`.
|
|
142
|
+
|
|
143
|
+
### Variable key failed
|
|
144
|
+
→ The key is likely the variable name instead of the variable key. Re-extract using:
|
|
145
|
+
```js
|
|
146
|
+
return (async function() {
|
|
147
|
+
var collections = await figma.variables.getLocalVariableCollectionsAsync();
|
|
148
|
+
for (var c = 0; c < collections.length; c++) {
|
|
149
|
+
for (var v = 0; v < collections[c].variableIds.length; v++) {
|
|
150
|
+
var variable = await figma.variables.getVariableByIdAsync(collections[c].variableIds[v]);
|
|
151
|
+
if (variable && variable.name === "VARIABLE_NAME") {
|
|
152
|
+
return { name: variable.name, correctKey: variable.key };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { error: "Variable not found" };
|
|
157
|
+
})();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Text style key failed
|
|
161
|
+
→ Likely a wrong key format. Re-extract using the script in `schemas/text-styles.md`.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Validation Output
|
|
166
|
+
|
|
167
|
+
After validation passes, log the summary:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Registry validation passed:
|
|
171
|
+
- Components: {N}/{N} keys verified ✓
|
|
172
|
+
- Variables: {N}/{N} keys verified ✓
|
|
173
|
+
- Text styles: {N}/{N} keys verified ✓
|
|
174
|
+
- Assets: {N}/{N} keys verified ✓ (or "skipped — no asset registries")
|
|
175
|
+
|
|
176
|
+
All import tests passed. Proceeding to guide generation.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
If validation fails after remediation attempts, ask the user to verify:
|
|
180
|
+
1. The DS library is published (not just local)
|
|
181
|
+
2. The library is enabled in the target Figma file
|
|
182
|
+
3. The correct Figma file was used for extraction
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Schema: variables.json
|
|
2
|
+
|
|
3
|
+
> **Read this BEFORE writing variables.json during setup.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Required Structure
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"meta": {
|
|
12
|
+
"source": "Library file name",
|
|
13
|
+
"fileKey": "FigmaFileKey",
|
|
14
|
+
"extractedAt": "YYYY-MM-DD",
|
|
15
|
+
"totalVariables": 856
|
|
16
|
+
},
|
|
17
|
+
"collections": {
|
|
18
|
+
"color": {
|
|
19
|
+
"id": "VariableCollectionId:19:113",
|
|
20
|
+
"modes": ["dark", "light"],
|
|
21
|
+
"variables": [
|
|
22
|
+
{ "name": "color/background/neutral/boldest", "key": "VariableID:19:114", "valuesByMode": { "dark": "#FFFFFF", "light": "#000000" } },
|
|
23
|
+
{ "name": "color/background/neutral/bolder", "key": "VariableID:19:115", "valuesByMode": { "dark": "#E5E5E5", "light": "#1A1A1A" } }
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"layout": {
|
|
27
|
+
"id": "VariableCollectionId:55:113",
|
|
28
|
+
"modes": ["value"],
|
|
29
|
+
"variables": [
|
|
30
|
+
{ "name": "layout/spacing/none", "key": "VariableID:55:114", "valuesByMode": { "value": 0 } },
|
|
31
|
+
{ "name": "layout/spacing/xsmall", "key": "VariableID:55:115", "valuesByMode": { "value": 8 } },
|
|
32
|
+
{ "name": "layout/spacing/medium", "key": "VariableID:55:118", "valuesByMode": { "value": 16 } },
|
|
33
|
+
{ "name": "layout/spacing/large", "key": "VariableID:55:119", "valuesByMode": { "value": 24 } },
|
|
34
|
+
{ "name": "layout/radius/medium", "key": "VariableID:55:130", "valuesByMode": { "value": 12 } }
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Field Requirements
|
|
44
|
+
|
|
45
|
+
### Per variable entry
|
|
46
|
+
|
|
47
|
+
| Field | Required | Description |
|
|
48
|
+
|-------|----------|-------------|
|
|
49
|
+
| `name` | **YES** | Variable path like `"color/background/neutral/boldest"` |
|
|
50
|
+
| `key` | **YES** | Variable key for `importVariableByKeyAsync`. Format varies (e.g., `"VariableID:55:114"` or a hash). |
|
|
51
|
+
| `valuesByMode` | **YES** | Object mapping mode names to resolved values |
|
|
52
|
+
|
|
53
|
+
### Per collection
|
|
54
|
+
|
|
55
|
+
| Field | Required | Description |
|
|
56
|
+
|-------|----------|-------------|
|
|
57
|
+
| `id` | optional | Collection ID |
|
|
58
|
+
| `modes` | **YES** | Array of mode names |
|
|
59
|
+
| `variables` | **YES** | Array of variable entries |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## CRITICAL: Variable `name` vs `key`
|
|
64
|
+
|
|
65
|
+
These are TWO DIFFERENT things:
|
|
66
|
+
|
|
67
|
+
| | `name` (path) | `key` (variable key) |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| **Looks like** | `"color/background/neutral/boldest"` | `"VariableID:55:114"` or hex hash |
|
|
70
|
+
| **Used for** | Human reading, organization | `importVariableByKeyAsync()` |
|
|
71
|
+
| **Available from** | `variable.name` | `variable.key` |
|
|
72
|
+
|
|
73
|
+
**`importVariableByKeyAsync` REQUIRES the `key`, NOT the `name`.** The name is useful for documentation but cannot be used for script imports.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## How to Extract Keys
|
|
78
|
+
|
|
79
|
+
### Via figma_execute (RECOMMENDED)
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
return (async function() {
|
|
83
|
+
var collections = await figma.variables.getLocalVariableCollectionsAsync();
|
|
84
|
+
var result = {};
|
|
85
|
+
|
|
86
|
+
for (var c = 0; c < collections.length; c++) {
|
|
87
|
+
var col = collections[c];
|
|
88
|
+
var collectionData = {
|
|
89
|
+
id: col.id,
|
|
90
|
+
modes: col.modes.map(function(m) { return m.name; }),
|
|
91
|
+
variables: []
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
for (var v = 0; v < col.variableIds.length; v++) {
|
|
95
|
+
var variable = await figma.variables.getVariableByIdAsync(col.variableIds[v]);
|
|
96
|
+
if (!variable) continue;
|
|
97
|
+
|
|
98
|
+
var valuesByMode = {};
|
|
99
|
+
for (var m = 0; m < col.modes.length; m++) {
|
|
100
|
+
var mode = col.modes[m];
|
|
101
|
+
var val = variable.valuesByMode[mode.modeId];
|
|
102
|
+
// Resolve color values to hex
|
|
103
|
+
if (val && typeof val === "object" && "r" in val) {
|
|
104
|
+
var r = Math.round(val.r * 255).toString(16).padStart(2, "0");
|
|
105
|
+
var g = Math.round(val.g * 255).toString(16).padStart(2, "0");
|
|
106
|
+
var b = Math.round(val.b * 255).toString(16).padStart(2, "0");
|
|
107
|
+
valuesByMode[mode.name] = "#" + r + g + b;
|
|
108
|
+
} else if (val && typeof val === "object" && "type" in val) {
|
|
109
|
+
// Alias — store the referenced variable name
|
|
110
|
+
valuesByMode[mode.name] = "→ alias";
|
|
111
|
+
} else {
|
|
112
|
+
valuesByMode[mode.name] = val;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
collectionData.variables.push({
|
|
117
|
+
name: variable.name,
|
|
118
|
+
key: variable.key, // ← THIS is what importVariableByKeyAsync needs
|
|
119
|
+
valuesByMode: valuesByMode
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Use first segment of variable names as collection key (e.g., "color", "layout")
|
|
124
|
+
var collName = col.name.toLowerCase().replace(/\s+/g, "-");
|
|
125
|
+
result[collName] = collectionData;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { collections: result };
|
|
129
|
+
})();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Important Notes
|
|
133
|
+
|
|
134
|
+
- For large collections (400+ variables), you may need to extract in batches per collection
|
|
135
|
+
- The `key` format varies: it might be `"VariableID:55:114"` or a different format depending on the Figma version
|
|
136
|
+
- Always verify keys work by running the validation script (see `schemas/validation.md`)
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Usage in Scripts
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
// Load variable by key (from registries/variables.json)
|
|
144
|
+
var spMedium = await figma.variables.importVariableByKeyAsync("VariableID:55:118");
|
|
145
|
+
|
|
146
|
+
// Bind to frame padding
|
|
147
|
+
frame.setBoundVariable('paddingTop', spMedium);
|
|
148
|
+
frame.setBoundVariable('itemSpacing', spMedium);
|
|
149
|
+
|
|
150
|
+
// Bind to color fill
|
|
151
|
+
var bgColor = await figma.variables.importVariableByKeyAsync("VariableID:19:114");
|
|
152
|
+
frame.fills = mf(bgColor); // using the mf() helper
|
|
153
|
+
```
|
|
@@ -68,9 +68,22 @@ What do you want to design? (component or screen)
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
3. **Write registries** (raw JSON, deterministic):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- `registries/
|
|
71
|
+
|
|
72
|
+
**CRITICAL: Read the schema for each registry BEFORE writing it:**
|
|
73
|
+
- `schemas/components.md` → `registries/components.json` (component names, **keys**, variants, properties)
|
|
74
|
+
- `schemas/variables.md` → `registries/variables.json` (variable names, **keys**, types, values by mode)
|
|
75
|
+
- `schemas/text-styles.md` → `registries/text-styles.json` (text style names, **keys**, font specs)
|
|
76
|
+
- `schemas/assets.md` → `registries/icons.json`, `registries/logos.json`, `registries/illustrations.json` (if applicable)
|
|
77
|
+
|
|
78
|
+
All paths relative to `knowledge-base/`. The schemas contain extraction scripts and required field definitions. The `key` field is MANDATORY for every component, variable, and style entry — without it, `design` generation will fail.
|
|
79
|
+
|
|
80
|
+
3b. **Validate registries (BLOCKING):**
|
|
81
|
+
|
|
82
|
+
Read `schemas/validation.md` and execute the full validation procedure:
|
|
83
|
+
1. **Structural check** — verify all registries match their schemas (every entry has `key`, correct types)
|
|
84
|
+
2. **Import test** — run validation script via `figma_execute` to test-import 3-5 sample keys per registry
|
|
85
|
+
3. **Remediation** — if ANY validation fails, re-extract the failing items using the remediation scripts
|
|
86
|
+
4. **Gate** — ALL validation checks MUST pass before proceeding to guide generation
|
|
74
87
|
|
|
75
88
|
4. **Generate intelligent guides** (Claude analyzes registries and writes):
|
|
76
89
|
|
|
@@ -114,10 +127,11 @@ What do you want to design? (component or screen)
|
|
|
114
127
|
|
|
115
128
|
7. **Validation summary:**
|
|
116
129
|
```
|
|
117
|
-
Knowledge base built:
|
|
118
|
-
- {N} components documented ({N} with variants)
|
|
119
|
-
- {N} variables ({N} colors, {N} spacing, {N} radius)
|
|
120
|
-
- {N} text styles
|
|
130
|
+
Knowledge base built and validated:
|
|
131
|
+
- {N} components documented ({N} with variants) — {N} keys verified via import test ✓
|
|
132
|
+
- {N} variables ({N} colors, {N} spacing, {N} radius) — {N} keys verified ✓
|
|
133
|
+
- {N} text styles — {N} keys verified ✓
|
|
134
|
+
- {N} asset items (icons/logos/illustrations) — {N} keys verified ✓
|
|
121
135
|
- {N} layout patterns extracted from {N} screenshots
|
|
122
136
|
|
|
123
137
|
Ready to design. Run: `spec {name}`
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
| Connected to Figma Desktop | Yes (before design) | Status has `setup.valid: true` |
|
|
11
11
|
| DS libraries enabled | Yes (before design) | User confirmation |
|
|
12
12
|
| Knowledge base exists | Yes (before spec) | `registries/` has JSON files |
|
|
13
|
+
| Registry schemas followed | Yes | All registry files match schemas in `schemas/` — every entry has `key` field |
|
|
14
|
+
| Registry keys validated | Yes | Sample import test passed via `figma_execute` (3-5 keys per registry) |
|
|
15
|
+
| No node IDs as keys | Yes | Component entries have `key` (hex hash), NOT just `id` (node ID like `1008:174`) |
|
|
16
|
+
| Variable keys present | Yes | Variables have `key` field, not just `name` paths |
|
|
13
17
|
|
|
14
18
|
---
|
|
15
19
|
|