@noemuch/bridge-ds 2.0.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/LICENSE +21 -0
- package/README.md +147 -0
- package/bin/bridge.js +4 -0
- package/commands/design-workflow.md +1 -0
- package/lib/cli.js +172 -0
- package/lib/mcp-setup.js +47 -0
- package/lib/scaffold.js +120 -0
- package/package.json +39 -0
- package/skills/design-workflow/SKILL.md +205 -0
- package/skills/design-workflow/references/actions/design.md +207 -0
- package/skills/design-workflow/references/actions/done.md +48 -0
- package/skills/design-workflow/references/actions/drop.md +38 -0
- package/skills/design-workflow/references/actions/review.md +149 -0
- package/skills/design-workflow/references/actions/spec.md +128 -0
- package/skills/design-workflow/references/figma-api-rules.md +453 -0
- package/skills/design-workflow/references/knowledge-base/README.md +52 -0
- package/skills/design-workflow/references/onboarding.md +351 -0
- package/skills/design-workflow/references/quality-gates.md +127 -0
- package/skills/design-workflow/references/templates/screen-template.md +127 -0
- package/skills/design-workflow/references/templates/spec-template.md +171 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Action: spec {name}
|
|
2
|
+
|
|
3
|
+
> Write or validate a specification. Auto-detects component vs screen mode.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Procedure
|
|
8
|
+
|
|
9
|
+
### 1. Determine mode
|
|
10
|
+
|
|
11
|
+
Ask or infer from context:
|
|
12
|
+
- **Component**: design system primitive, block, or composition (Button, Card, Tag...)
|
|
13
|
+
- **Screen**: full interface or page (dashboard, settings, onboarding flow...)
|
|
14
|
+
|
|
15
|
+
### 2. Gather context
|
|
16
|
+
|
|
17
|
+
- **Load design patterns** (MANDATORY for screens, recommended for components):
|
|
18
|
+
- `references/knowledge-base/guides/design-patterns.md` → pattern catalogue
|
|
19
|
+
- Identify the **closest pattern** for this screen/component
|
|
20
|
+
- **Read the referenced screenshots** (min 2) from `references/knowledge-base/ui-references/screenshots/`
|
|
21
|
+
- Study: zones, proportions, density, hierarchy, navigation, card rhythm
|
|
22
|
+
- The spec's **layout structure and architecture diagram** MUST be based on these patterns, not invented from scratch
|
|
23
|
+
|
|
24
|
+
- **Load DS knowledge base** — use these to make informed component and token choices:
|
|
25
|
+
- `references/knowledge-base/guides/components/overview.md` → component decision tree
|
|
26
|
+
- `references/knowledge-base/registries/components.json` → available components with Figma keys
|
|
27
|
+
- `references/knowledge-base/guides/tokens/spacing-usage.md` → spacing scale + usage contexts
|
|
28
|
+
- `references/knowledge-base/guides/tokens/color-usage.md` → color token decision tree
|
|
29
|
+
- `references/knowledge-base/guides/tokens/typography-usage.md` → type hierarchy + font families
|
|
30
|
+
- Load relevant pattern guides if screen mode (form-patterns.md, multi-step-flow.md, navigation-patterns.md, feedback-patterns.md)
|
|
31
|
+
- Load relevant component group guides based on the screen's needs
|
|
32
|
+
|
|
33
|
+
- **Check knowledge base exists** — if `references/knowledge-base/registries/` is empty, tell the user: "Knowledge base not built yet. Run: `setup` first."
|
|
34
|
+
|
|
35
|
+
- Check existing specs: `specs/active/`, `specs/backlog/`
|
|
36
|
+
- Check existing DS components in `references/knowledge-base/registries/components.json`
|
|
37
|
+
|
|
38
|
+
### 3. Write the spec
|
|
39
|
+
|
|
40
|
+
Write to `specs/active/{name}-spec.md`.
|
|
41
|
+
|
|
42
|
+
> For multi-screen flows with their own splitting rules (e.g., `specs/active/f1/rules.md`), read those rules first for folder structure and naming conventions.
|
|
43
|
+
|
|
44
|
+
#### Component spec — mandatory sections:
|
|
45
|
+
|
|
46
|
+
- **Description** (what, why, design principle)
|
|
47
|
+
- **Architecture overview** (composition diagram)
|
|
48
|
+
- **Component hierarchy** (levels: primitive → blocks → composition)
|
|
49
|
+
- **Props API** (TypeScript interfaces, variant names matching Figma)
|
|
50
|
+
- **Layout specs** (dimensions, gaps, alignment)
|
|
51
|
+
- **Design tokens** (spacing, colors, typography, radius)
|
|
52
|
+
- **Component properties** (TEXT, INSTANCE_SWAP, BOOLEAN — Figma)
|
|
53
|
+
- **Reused DS components** (existing components from registry)
|
|
54
|
+
- **Acceptance criteria** (checkboxes, testable)
|
|
55
|
+
- **Open questions**
|
|
56
|
+
|
|
57
|
+
Use template from `references/templates/spec-template.md`.
|
|
58
|
+
|
|
59
|
+
#### Screen spec — mandatory sections:
|
|
60
|
+
|
|
61
|
+
- **Description** (what screen, user goal, context)
|
|
62
|
+
- **Visual reference** (pattern name, screenshots studied, key composition rules applied)
|
|
63
|
+
- **Layout structure** (zones, grid, responsive behavior — MUST follow matched pattern)
|
|
64
|
+
- **Sections breakdown** (header, content area, sidebar, footer...)
|
|
65
|
+
- **DS components used** (EXHAUSTIVE list — every visible element must be accounted for with Figma keys from registry)
|
|
66
|
+
- **Content structure** (real or realistic placeholder data)
|
|
67
|
+
- **States** (empty, loading, populated, error)
|
|
68
|
+
- **Design tokens** (background, spacing rhythm, elevation)
|
|
69
|
+
- **Responsive rules** (breakpoints, layout shifts)
|
|
70
|
+
- **Acceptance criteria** (checkboxes)
|
|
71
|
+
- **Open questions**
|
|
72
|
+
|
|
73
|
+
Use template from `references/templates/screen-template.md`.
|
|
74
|
+
|
|
75
|
+
#### Component spec — visual reference (recommended):
|
|
76
|
+
|
|
77
|
+
For components that appear in existing screens, also include:
|
|
78
|
+
- **Visual reference** (which pattern/screenshot shows a similar component, composition rules)
|
|
79
|
+
- This helps the `design` step match proportions and density
|
|
80
|
+
|
|
81
|
+
### 3b. Identify new DS components (screen mode only)
|
|
82
|
+
|
|
83
|
+
For each UI pattern described in the spec, check if it exists in `registries/components.json`:
|
|
84
|
+
- If a pattern is covered by an existing DS component → reference it in "DS Components Used"
|
|
85
|
+
- If a pattern is NOT covered → add it to the **"New DS Components Required"** section
|
|
86
|
+
|
|
87
|
+
**This is a blocking gate.** If new components are identified:
|
|
88
|
+
1. List them in the spec with: name, description, where they're used, and variants/states needed
|
|
89
|
+
2. After the screen spec is validated, each new component triggers its own `spec {component}` → `design` → `done` cycle
|
|
90
|
+
3. The screen `design` step is blocked until all new components exist in the DS
|
|
91
|
+
|
|
92
|
+
**Common signals that a new component is needed:**
|
|
93
|
+
- A card/tile with specific internal structure (icon + title + description + badge + CTA)
|
|
94
|
+
- A custom indicator or status pattern not covered by Badge/Tag
|
|
95
|
+
- A navigation pattern not covered by existing nav components
|
|
96
|
+
- A data display pattern not covered by existing list/table components
|
|
97
|
+
|
|
98
|
+
### 4. Validate
|
|
99
|
+
|
|
100
|
+
**Token architecture:**
|
|
101
|
+
- [ ] Every visual value references a design token (no hardcoded px/hex)
|
|
102
|
+
- [ ] Tokens use semantic names, not visual (`danger` not `red`)
|
|
103
|
+
- [ ] Aliasing depth ≤ 2 levels (primitive → semantic → component max)
|
|
104
|
+
|
|
105
|
+
**Naming conventions:**
|
|
106
|
+
- [ ] Variant names match Figma exactly
|
|
107
|
+
- [ ] Props use DS naming conventions (camelCase, `is` prefix for booleans, `on` prefix for events)
|
|
108
|
+
- [ ] Token references follow `{category}/{role}/{emphasis}` pattern
|
|
109
|
+
|
|
110
|
+
**Component API** (component mode only):
|
|
111
|
+
- [ ] Props surface area is minimal (composability over configuration)
|
|
112
|
+
- [ ] Composition pattern appropriate (slots > mega-props)
|
|
113
|
+
- [ ] Platform parity considered
|
|
114
|
+
|
|
115
|
+
**General:**
|
|
116
|
+
- [ ] Acceptance criteria are testable (not vague)
|
|
117
|
+
- [ ] Reused DS components identified
|
|
118
|
+
- [ ] Open questions are explicit
|
|
119
|
+
|
|
120
|
+
### 5. Present for review
|
|
121
|
+
|
|
122
|
+
Output the spec, flag assumptions, highlight open questions.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Transition
|
|
127
|
+
|
|
128
|
+
When spec is approved → suggest: "Spec approved. Run: `design`"
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# Figma Plugin API — Mandatory Rules
|
|
2
|
+
|
|
3
|
+
> **This file MUST be read before writing ANY Figma generation script.**
|
|
4
|
+
> Every rule here was learned from real bugs. Breaking them = broken layout.
|
|
5
|
+
>
|
|
6
|
+
> **Note:** Variable keys, text style keys, and component keys vary per design system.
|
|
7
|
+
> Always load them from your project's `knowledge-base/registries/` directory.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Rule 1: FILL after appendChild (CRITICAL)
|
|
12
|
+
|
|
13
|
+
`layoutSizingHorizontal = "FILL"` and `layoutSizingVertical = "FILL"` **ONLY work on children of auto-layout frames**. Setting FILL before `appendChild()` throws an error.
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// WRONG — crashes
|
|
17
|
+
child.layoutSizingHorizontal = "FILL";
|
|
18
|
+
parent.appendChild(child);
|
|
19
|
+
|
|
20
|
+
// CORRECT
|
|
21
|
+
parent.appendChild(child);
|
|
22
|
+
child.layoutSizingHorizontal = "FILL";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Helper:**
|
|
26
|
+
```js
|
|
27
|
+
function appendFill(parent, child, fillH, fillV) {
|
|
28
|
+
parent.appendChild(child);
|
|
29
|
+
if (fillH) child.layoutSizingHorizontal = "FILL";
|
|
30
|
+
if (fillV) child.layoutSizingVertical = "FILL";
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Rule 2: Absolute positioning after appendChild
|
|
37
|
+
|
|
38
|
+
`layoutPositioning = "ABSOLUTE"` requires the node to already be inside an auto-layout parent.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
// CORRECT
|
|
42
|
+
parent.appendChild(circle);
|
|
43
|
+
circle.layoutPositioning = "ABSOLUTE";
|
|
44
|
+
circle.x = 100; circle.y = 50;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Rule 3: FILL + AUTO parent = collapsed layout
|
|
50
|
+
|
|
51
|
+
A child with `FILL` inside a parent with `primaryAxisSizingMode = "AUTO"` collapses to 0px. The parent must be FIXED for FILL children.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// CORRECT
|
|
55
|
+
root.primaryAxisSizingMode = "FIXED";
|
|
56
|
+
root.resize(1440, 900);
|
|
57
|
+
// then after appendChild:
|
|
58
|
+
mainArea.layoutSizingVertical = "FILL";
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Rule 4: resize() overrides sizing modes (CRITICAL)
|
|
64
|
+
|
|
65
|
+
`resize(width, height)` forces **both** axes to `"FIXED"`. Set sizing modes AFTER resize.
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// CORRECT
|
|
69
|
+
frame.resize(700, 10);
|
|
70
|
+
frame.primaryAxisSizingMode = "AUTO";
|
|
71
|
+
frame.counterAxisSizingMode = "FIXED";
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Rule 5: counterAxisAlignItems for cross-axis centering
|
|
77
|
+
|
|
78
|
+
| Layout mode | primaryAxisAlignItems controls | counterAxisAlignItems controls |
|
|
79
|
+
|-------------|-------------------------------|-------------------------------|
|
|
80
|
+
| VERTICAL | Vertical alignment | Horizontal alignment |
|
|
81
|
+
| HORIZONTAL | Horizontal alignment | Vertical alignment |
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
// Center children horizontally in a vertical layout
|
|
85
|
+
container.layoutMode = "VERTICAL";
|
|
86
|
+
container.counterAxisAlignItems = "CENTER";
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Rule 6: Always bind spacing variables (NEVER hardcode px)
|
|
92
|
+
|
|
93
|
+
Every padding, gap, and radius value MUST use `setBoundVariable()` with a spacing token from the registry.
|
|
94
|
+
|
|
95
|
+
**Load spacing keys from `registries/variables.json`.** Look for variables with names like `layout/spacing/*` and `layout/radius/*`.
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// Load spacing vars once at script start (keys from your registries/variables.json)
|
|
99
|
+
var spLarge = await figma.variables.importVariableByKeyAsync("YOUR_SPACING_LARGE_KEY");
|
|
100
|
+
var spMedium = await figma.variables.importVariableByKeyAsync("YOUR_SPACING_MEDIUM_KEY");
|
|
101
|
+
var radMedium = await figma.variables.importVariableByKeyAsync("YOUR_RADIUS_MEDIUM_KEY");
|
|
102
|
+
|
|
103
|
+
// Bind to frame
|
|
104
|
+
frame.setBoundVariable('itemSpacing', spMedium);
|
|
105
|
+
frame.setBoundVariable('paddingTop', spLarge);
|
|
106
|
+
frame.setBoundVariable('paddingBottom', spLarge);
|
|
107
|
+
frame.setBoundVariable('paddingLeft', spLarge);
|
|
108
|
+
frame.setBoundVariable('paddingRight', spLarge);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Helper:**
|
|
112
|
+
```js
|
|
113
|
+
function bindPadding(frame, top, right, bottom, left) {
|
|
114
|
+
if (top) frame.setBoundVariable("paddingTop", top);
|
|
115
|
+
if (right) frame.setBoundVariable("paddingRight", right);
|
|
116
|
+
if (bottom) frame.setBoundVariable("paddingBottom", bottom);
|
|
117
|
+
if (left) frame.setBoundVariable("paddingLeft", left);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function bindRadius(frame, radiusVar) {
|
|
121
|
+
frame.setBoundVariable("topLeftRadius", radiusVar);
|
|
122
|
+
frame.setBoundVariable("topRightRadius", radiusVar);
|
|
123
|
+
frame.setBoundVariable("bottomLeftRadius", radiusVar);
|
|
124
|
+
frame.setBoundVariable("bottomRightRadius", radiusVar);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Rule 7: Colors via setBoundVariableForPaint (not setBoundVariable)
|
|
131
|
+
|
|
132
|
+
Fills and strokes use a **different API** than layout properties.
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
// WRONG
|
|
136
|
+
frame.setBoundVariable('fills', colorVar);
|
|
137
|
+
|
|
138
|
+
// CORRECT
|
|
139
|
+
function mf(colorVar) {
|
|
140
|
+
var p = figma.util.solidPaint("#000000");
|
|
141
|
+
p = figma.variables.setBoundVariableForPaint(p, "color", colorVar);
|
|
142
|
+
return [p];
|
|
143
|
+
}
|
|
144
|
+
frame.fills = mf(myColorVar);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Load color keys from `registries/variables.json`.** Look for variables with names like `color/background/*`, `color/text/*`, `color/border/*`.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Rule 8: Text styles via importStyleByKeyAsync (NEVER hardcode fonts)
|
|
152
|
+
|
|
153
|
+
Never set `fontName`, `fontSize`, or `lineHeight` manually. Always use text styles from the library.
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
// WRONG
|
|
157
|
+
text.fontName = { family: "Inter", style: "Bold" };
|
|
158
|
+
text.fontSize = 32;
|
|
159
|
+
|
|
160
|
+
// CORRECT (key from registries/text-styles.json)
|
|
161
|
+
var style = await figma.importStyleByKeyAsync("YOUR_TEXT_STYLE_KEY");
|
|
162
|
+
text.textStyleId = style.id;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Load text style keys from `registries/text-styles.json`.**
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Rule 9: Component properties — add, bind, and override
|
|
170
|
+
|
|
171
|
+
### 9a. addComponentProperty AFTER combineAsVariants
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
var compSet = figma.combineAsVariants(components, figma.currentPage);
|
|
175
|
+
compSet.addComponentProperty('title', 'TEXT', 'Default title');
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 9b. Bind TEXT properties to text nodes
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
var titlePropKey = Object.keys(compSet.componentPropertyDefinitions)
|
|
182
|
+
.find(k => compSet.componentPropertyDefinitions[k].type === "TEXT" && k.startsWith("title"));
|
|
183
|
+
|
|
184
|
+
for (var i = 0; i < compSet.children.length; i++) {
|
|
185
|
+
var variant = compSet.children[i];
|
|
186
|
+
var titleNode = variant.findOne(n => n.name === "title" && n.type === "TEXT");
|
|
187
|
+
if (titleNode && titlePropKey) {
|
|
188
|
+
titleNode.componentPropertyReferences = { characters: titlePropKey };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 9c. Override TEXT properties on instances
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
var instance = variant.createInstance();
|
|
197
|
+
var propDefs = compSet.componentPropertyDefinitions;
|
|
198
|
+
for (var key in propDefs) {
|
|
199
|
+
if (key.startsWith("title") && propDefs[key].type === "TEXT") {
|
|
200
|
+
instance.setProperties({ [key]: "My custom title" });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 9d. INSTANCE_SWAP properties for icons
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
compSet.addComponentProperty('icon', 'INSTANCE_SWAP', defaultIconId);
|
|
209
|
+
var iconPropKey = Object.keys(compSet.componentPropertyDefinitions)
|
|
210
|
+
.find(k => k.startsWith("icon") && compSet.componentPropertyDefinitions[k].type === "INSTANCE_SWAP");
|
|
211
|
+
|
|
212
|
+
for (var i = 0; i < compSet.children.length; i++) {
|
|
213
|
+
var variant = compSet.children[i];
|
|
214
|
+
var iconNode = variant.findOne(n => n.name === "icon" && n.type === "INSTANCE");
|
|
215
|
+
if (iconNode && iconPropKey) {
|
|
216
|
+
iconNode.componentPropertyReferences = { mainComponent: iconPropKey };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 9e. BOOLEAN properties for visibility
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
compSet.addComponentProperty('showButton', 'BOOLEAN', true);
|
|
225
|
+
var btnPropKey = Object.keys(compSet.componentPropertyDefinitions)
|
|
226
|
+
.find(k => k.startsWith("showButton"));
|
|
227
|
+
|
|
228
|
+
for (var i = 0; i < compSet.children.length; i++) {
|
|
229
|
+
var variant = compSet.children[i];
|
|
230
|
+
var btnNode = variant.findOne(n => n.name === "button");
|
|
231
|
+
if (btnNode && btnPropKey) {
|
|
232
|
+
btnNode.componentPropertyReferences = { visible: btnPropKey };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Rule 10: Property keys include hash suffix
|
|
240
|
+
|
|
241
|
+
`componentPropertyDefinitions` returns keys like `title#9311:226`. Use the **full key** (with hash).
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
function findPropKey(compSet, prefix, type) {
|
|
245
|
+
var defs = compSet.componentPropertyDefinitions;
|
|
246
|
+
return Object.keys(defs).find(function(k) {
|
|
247
|
+
return k.startsWith(prefix) && defs[k].type === type;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
var titleKey = findPropKey(compSet, "title", "TEXT");
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Rule 11: importComponentSetByKeyAsync vs importComponentByKeyAsync
|
|
256
|
+
|
|
257
|
+
| What | API |
|
|
258
|
+
|------|-----|
|
|
259
|
+
| Component with variants (Button, Tag) | `importComponentSetByKeyAsync` |
|
|
260
|
+
| Single component (icon, logo) | `importComponentByKeyAsync` |
|
|
261
|
+
|
|
262
|
+
Check `registries/components.json` — entries with `variantCount > 1` are component sets.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Rule 12: textAutoResize in auto-layout
|
|
267
|
+
|
|
268
|
+
Set characters FIRST, append, FILL, then textAutoResize:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
var t = figma.createText();
|
|
272
|
+
t.characters = "Long text...";
|
|
273
|
+
parent.appendChild(t);
|
|
274
|
+
t.layoutSizingHorizontal = "FILL";
|
|
275
|
+
t.textAutoResize = "HEIGHT";
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**GOTCHA:** `textAutoResize = "HEIGHT"` before the node has width → 0-width vertical text.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Rule 13: strokeAlign INSIDE for cards
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
card.strokes = mf(borderVar);
|
|
286
|
+
card.strokeWeight = 1;
|
|
287
|
+
card.strokeAlign = "INSIDE";
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Always use `"INSIDE"` for cards, panels, inputs.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Rule 14: Cannot add children to instances
|
|
295
|
+
|
|
296
|
+
Component instances are sealed. Use `setProperties()` or `swapComponent()`.
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
// WRONG
|
|
300
|
+
inst.appendChild(extraFrame); // crashes
|
|
301
|
+
|
|
302
|
+
// CORRECT
|
|
303
|
+
inst.setProperties({ [titleKey]: "New title" });
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Rule 15: Variant grid layout after combineAsVariants
|
|
309
|
+
|
|
310
|
+
Variants stack at (0,0) after `combineAsVariants()`. Arrange in a grid:
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
var cols = 4;
|
|
314
|
+
for (var i = 0; i < compSet.children.length; i++) {
|
|
315
|
+
var child = compSet.children[i];
|
|
316
|
+
child.x = (i % cols) * (child.width + 40);
|
|
317
|
+
child.y = Math.floor(i / cols) * (child.height + 40);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Rule 16: loadFontAsync before ANY text operation
|
|
324
|
+
|
|
325
|
+
Every script that creates or modifies text MUST load fonts first:
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
await figma.loadFontAsync({ family: "Inter", style: "Regular" });
|
|
329
|
+
await figma.loadFontAsync({ family: "Inter", style: "Medium" });
|
|
330
|
+
await figma.loadFontAsync({ family: "Inter", style: "Semi Bold" });
|
|
331
|
+
await figma.loadFontAsync({ family: "Inter", style: "Bold" });
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Load the fonts used in YOUR design system. Check `registries/text-styles.json` for font families.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Rule 17: Script structure
|
|
339
|
+
|
|
340
|
+
Every script executed via `figma_execute` MUST follow this structure:
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
return (async function() {
|
|
344
|
+
// 1. Load fonts
|
|
345
|
+
// 2. Import variables + styles + components (keys from registries)
|
|
346
|
+
// 3. Define helpers (mf, appendFill, bindPadding, bindRadius)
|
|
347
|
+
// 4. Build layout tree (create → configure → append → FILL)
|
|
348
|
+
// 5. Return summary
|
|
349
|
+
return { success: true };
|
|
350
|
+
})();
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The `return` before the IIFE is mandatory — without it, the Promise is lost.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Rule 18: DS component reuse — NEVER recreate existing components
|
|
358
|
+
|
|
359
|
+
**This is the most critical rule. Violations require script rewrite.**
|
|
360
|
+
|
|
361
|
+
Before creating ANY visual element, check the registries:
|
|
362
|
+
1. `registries/components.json` → Buttons, Tags, Inputs, Avatars, Dividers, etc.
|
|
363
|
+
2. `registries/icons.json` → Icons (if exists)
|
|
364
|
+
3. `registries/logos.json` → Logos (if exists)
|
|
365
|
+
4. `registries/illustrations.json` → Illustrations (if exists)
|
|
366
|
+
|
|
367
|
+
**NEVER:**
|
|
368
|
+
- Create a raw frame/ellipse for an Avatar → import the DS component
|
|
369
|
+
- Create a raw rectangle for a Divider → import the DS component
|
|
370
|
+
- Create a raw frame with text for a Tag/Badge → import the DS component
|
|
371
|
+
- Hardcode hex colors → always bind variables
|
|
372
|
+
|
|
373
|
+
**Pre-script checklist (mandatory):**
|
|
374
|
+
```
|
|
375
|
+
Elements in this step:
|
|
376
|
+
- Avatar → components.json key: xxx ✓
|
|
377
|
+
- Divider → components.json key: xxx ✓
|
|
378
|
+
- Label → raw text (no DS component for this) ✓
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Rule 19: Canvas positioning — never stack components
|
|
384
|
+
|
|
385
|
+
- **NEVER** leave multiple components at (0, 0)
|
|
386
|
+
- **Minimum gap**: 80px between components on the canvas
|
|
387
|
+
- **Sub-components**: horizontal row at the top
|
|
388
|
+
- **Main component**: 400px below sub-components
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
var currentX = 0;
|
|
392
|
+
for (var i = 0; i < subComponents.length; i++) {
|
|
393
|
+
subComponents[i].x = currentX;
|
|
394
|
+
subComponents[i].y = 0;
|
|
395
|
+
currentX += subComponents[i].width + 80;
|
|
396
|
+
}
|
|
397
|
+
mainComponent.x = 0;
|
|
398
|
+
mainComponent.y = 400;
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Standard Script Boilerplate
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
return (async function() {
|
|
407
|
+
|
|
408
|
+
// ─── FONTS ───
|
|
409
|
+
// Load fonts used by your DS (check registries/text-styles.json for families)
|
|
410
|
+
await figma.loadFontAsync({ family: "Inter", style: "Regular" });
|
|
411
|
+
await figma.loadFontAsync({ family: "Inter", style: "Medium" });
|
|
412
|
+
await figma.loadFontAsync({ family: "Inter", style: "Semi Bold" });
|
|
413
|
+
await figma.loadFontAsync({ family: "Inter", style: "Bold" });
|
|
414
|
+
|
|
415
|
+
// ─── VARIABLES (from registries/variables.json) ───
|
|
416
|
+
// Load spacing, radius, and color variables by key
|
|
417
|
+
// var spLarge = await figma.variables.importVariableByKeyAsync("YOUR_KEY");
|
|
418
|
+
// var radMedium = await figma.variables.importVariableByKeyAsync("YOUR_KEY");
|
|
419
|
+
// var bgColor = await figma.variables.importVariableByKeyAsync("YOUR_KEY");
|
|
420
|
+
|
|
421
|
+
// ─── HELPERS ───
|
|
422
|
+
function mf(colorVar) {
|
|
423
|
+
var p = figma.util.solidPaint("#000000");
|
|
424
|
+
p = figma.variables.setBoundVariableForPaint(p, "color", colorVar);
|
|
425
|
+
return [p];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function appendFill(parent, child, fillH, fillV) {
|
|
429
|
+
parent.appendChild(child);
|
|
430
|
+
if (fillH) child.layoutSizingHorizontal = "FILL";
|
|
431
|
+
if (fillV) child.layoutSizingVertical = "FILL";
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function bindPadding(frame, top, right, bottom, left) {
|
|
435
|
+
if (top) frame.setBoundVariable("paddingTop", top);
|
|
436
|
+
if (right) frame.setBoundVariable("paddingRight", right);
|
|
437
|
+
if (bottom) frame.setBoundVariable("paddingBottom", bottom);
|
|
438
|
+
if (left) frame.setBoundVariable("paddingLeft", left);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function bindRadius(frame, radiusVar) {
|
|
442
|
+
frame.setBoundVariable("topLeftRadius", radiusVar);
|
|
443
|
+
frame.setBoundVariable("topRightRadius", radiusVar);
|
|
444
|
+
frame.setBoundVariable("bottomLeftRadius", radiusVar);
|
|
445
|
+
frame.setBoundVariable("bottomRightRadius", radiusVar);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ─── BUILD ───
|
|
449
|
+
// ... your design code here ...
|
|
450
|
+
|
|
451
|
+
return { success: true };
|
|
452
|
+
})();
|
|
453
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Knowledge Base
|
|
2
|
+
|
|
3
|
+
This directory contains your design system's complete documentation, generated by Claude during `/design-workflow setup`.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
knowledge-base/
|
|
9
|
+
registries/ ← Raw DS data (auto-extracted from Figma)
|
|
10
|
+
components.json ← Component names, keys, variants, properties
|
|
11
|
+
variables.json ← Variable names, keys, types, values by mode
|
|
12
|
+
text-styles.json ← Text style names, keys, font specs
|
|
13
|
+
icons.json ← Icon component keys (optional)
|
|
14
|
+
logos.json ← Logo component keys (optional)
|
|
15
|
+
illustrations.json ← Illustration keys (optional)
|
|
16
|
+
|
|
17
|
+
guides/ ← Intelligent documentation (generated by Claude)
|
|
18
|
+
design-patterns.md ← Layout patterns extracted from product screenshots
|
|
19
|
+
tokens/
|
|
20
|
+
color-usage.md ← Color token decision tree
|
|
21
|
+
spacing-usage.md ← Spacing scale + usage contexts
|
|
22
|
+
typography-usage.md ← Type hierarchy + font families
|
|
23
|
+
components/
|
|
24
|
+
overview.md ← Component decision tree (what to use when)
|
|
25
|
+
{group}.md ← Per-group guides (actions, form-controls, etc.)
|
|
26
|
+
patterns/
|
|
27
|
+
form-patterns.md ← Form field patterns, validation
|
|
28
|
+
navigation-patterns.md ← Sidebar, tabs, breadcrumbs
|
|
29
|
+
feedback-patterns.md ← Success, error, warning, loading states
|
|
30
|
+
multi-step-flow.md ← Stepper, progress, module flows
|
|
31
|
+
assets/
|
|
32
|
+
icons.md ← Categorized icon catalog
|
|
33
|
+
logos.md ← Logo catalog
|
|
34
|
+
illustrations.md ← Illustration catalog + usage
|
|
35
|
+
|
|
36
|
+
ui-references/ ← Product screenshots for pattern extraction
|
|
37
|
+
screenshots/ ← PNG/JPG files of your product's key screens
|
|
38
|
+
ui-references-guide.md ← Which screenshot for which pattern type
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## How to populate
|
|
42
|
+
|
|
43
|
+
Run `/design-workflow setup` in Claude Code. Claude will:
|
|
44
|
+
|
|
45
|
+
1. **Extract** your DS from Figma via `figma_get_design_system_kit`
|
|
46
|
+
2. **Analyze** the raw data and write intelligent guides
|
|
47
|
+
3. **Ask for screenshots** of your product's key screens
|
|
48
|
+
4. **Generate** layout pattern documentation from the screenshots
|
|
49
|
+
|
|
50
|
+
## How to update
|
|
51
|
+
|
|
52
|
+
When your DS evolves (new components, changed tokens), run `/design-workflow setup` again. Claude will detect existing registries and offer to update them incrementally.
|