@mehuljatiya/troupe 0.1.15

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/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Troupe
2
+
3
+ AI workflows for everyone on the team — designers, developers, and PMs.
4
+
5
+ One setup command installs 9 slash commands you can use inside Claude Code, right from your terminal.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ```bash
12
+ npx @mehuljatiya/troupe@latest setup
13
+ ```
14
+
15
+ The wizard (~2 min) handles everything: Claude Code, Figma MCP, browser plugins, and all 9 commands.
16
+
17
+ ---
18
+
19
+ ## What you get
20
+
21
+ **9 slash commands** installed to `~/.claude/commands/`:
22
+
23
+ | Command | For | What it does |
24
+ |---|---|---|
25
+ | `/figma [url]` | Designers, Devs | Pull a Figma design and build it as code |
26
+ | `/document-component [url]` | Designers, PMs | Full component docs from a Figma URL — `.md` + `.html` preview |
27
+ | `/spec [url]` | PMs | Ticket-ready spec with edge cases and acceptance criteria |
28
+ | `/qa [url]` | PMs, Devs | Compare Figma to built component, flag gaps |
29
+ | `/new-component` | Designers, Devs | Start a new component from scratch |
30
+ | `/document` | Designers, Devs | Write docs for an existing codebase component |
31
+ | `/review` | Everyone | Design quality and consistency check |
32
+ | `/tokens` | Designers, Devs | Explain and audit design tokens in any project |
33
+ | `/handoff` | Designers, PMs | Generate a developer handoff spec |
34
+
35
+ ---
36
+
37
+ ## Usage
38
+
39
+ Open Terminal in any project folder and type:
40
+
41
+ ```bash
42
+ claude
43
+ ```
44
+
45
+ Or type `design` for a printed cheat sheet of all commands, then Claude opens.
46
+
47
+ ---
48
+
49
+ ## Figma
50
+
51
+ Commands that take a Figma URL need Figma MCP — the setup wizard configures this automatically.
52
+
53
+ To add it manually:
54
+
55
+ ```bash
56
+ claude mcp add --transport http figma https://mcp.figma.com/mcp --scope user
57
+ ```
58
+
59
+ Then inside Claude: `/mcp` → **figma** → **Authenticate** (one-time OAuth).
60
+
61
+ ---
62
+
63
+ ## Requirements
64
+
65
+ - Node.js 20+ (setup wizard can install this automatically via nvm or Homebrew)
66
+ - Anthropic API key — [console.anthropic.com](https://console.anthropic.com) (free to start)
67
+ - Figma account (for Figma commands)
68
+
69
+ ---
70
+
71
+ ## Updating
72
+
73
+ To get the latest commands, delete existing ones and re-run:
74
+
75
+ ```bash
76
+ rm ~/.claude/commands/{figma,document-component,spec,qa,new-component,document,review,tokens,handoff}.md
77
+ npx @mehuljatiya/troupe@latest setup
78
+ ```
79
+
80
+ ---
81
+
82
+ ## License
83
+
84
+ MIT · by [Mehul Jatiya](https://github.com/mehuljatiya)
package/bin/cli.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { runSetup } from '../src/setup.js'
3
+
4
+ const command = process.argv[2]
5
+
6
+ if (!command || command === 'setup') {
7
+ runSetup()
8
+ } else if (command === '--help' || command === '-h') {
9
+ console.log(`
10
+ @mehuljatiya/troupe
11
+
12
+ Usage:
13
+ npx @mehuljatiya/troupe setup Set up Troupe for the first time
14
+ design Launch Claude with design workflows
15
+ `)
16
+ } else {
17
+ console.log(`Unknown command: ${command}`)
18
+ console.log('Run: npx @mehuljatiya/troupe setup')
19
+ process.exit(1)
20
+ }
package/bin/design.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import chalk from 'chalk'
4
+
5
+ console.log('\n' + chalk.bold('Troupe') + ' ✦')
6
+ console.log(chalk.dim('Available workflows — type any of these inside Claude:\n'))
7
+ console.log(' ' + chalk.cyan('/figma') + ' [url] Pull a Figma design and build it')
8
+ console.log(' ' + chalk.cyan('/document-component') + ' [url] Generate full component documentation from Figma')
9
+ console.log(' ' + chalk.cyan('/spec') + ' [url] Ticket-ready spec with edge cases + acceptance criteria')
10
+ console.log(' ' + chalk.cyan('/qa') + ' [url] Compare Figma design to built component, flag gaps')
11
+ console.log(' ' + chalk.cyan('/new-component') + ' Start a new component from scratch')
12
+ console.log(' ' + chalk.cyan('/document') + ' Write docs for a component')
13
+ console.log(' ' + chalk.cyan('/review') + ' Design quality and consistency check')
14
+ console.log(' ' + chalk.cyan('/tokens') + ' Explain the design tokens in this project')
15
+ console.log(' ' + chalk.cyan('/handoff') + ' Generate a developer handoff doc')
16
+ console.log()
17
+
18
+ try {
19
+ execSync('claude', { stdio: 'inherit' })
20
+ } catch (err) {
21
+ if (err.code === 'ENOENT') {
22
+ console.log(chalk.red('\nClaude Code not found.'))
23
+ console.log('Run: ' + chalk.cyan('npx @mehuljatiya/troupe setup'))
24
+ process.exit(1)
25
+ }
26
+ // normal exit or Ctrl+C — ignore
27
+ }
@@ -0,0 +1,505 @@
1
+ ---
2
+ description: Generates a detailed component documentation figma frame in the same page where given component lives. Renders it as HTML for browser review, then pushes documentation as real Figma nodes into the same design file page where the component lives. Use when the user shares a Figma link to a component documentation page and asks for docs, a .md file, or a written reference.
3
+ ---
4
+
5
+ # Document Component
6
+
7
+ You are generating developer-ready component documentation from a Figma design system page, rendering it for browser review, and pushing it as real Figma nodes into the **same design file page where the component lives**.
8
+
9
+ > **CRITICAL — Where to push docs:**
10
+ > Always push into the **Figma design file** (figma.com/design/...) on the same page as the component.
11
+ > **NEVER push to FigJam.** The `generate_diagram` MCP tool only supports FigJam — do NOT use it.
12
+
13
+ ---
14
+
15
+ ## Token budget rules — read before starting
16
+
17
+ These apply to every run. No exceptions.
18
+
19
+ 1. **Research max 4 design systems.** Search at most 4 external sources (not 8). Prioritise: Material Design, Polaris, Carbon, Primer — pick whichever 4 are most relevant to the component type. Fetch only the specific component page, not the entire doc site.
20
+ 2. **`excludeScreenshot: true` on data-only sections.** Screenshots are only useful where visual verification matters. Use `excludeScreenshot: true` on Props & Tokens, Accessibility, Usage Guidelines, and Platform sections — code alone is sufficient there. Keep screenshots enabled for Variations and Anatomy, where you need to visually verify all variants and component structure are captured correctly.
21
+ 3. **Two `use_figma` calls maximum to build the doc frame.** Call 1: inspect — get all page names, component IDs, node IDs needed. Call 2: build the entire frame. Avoid iterative inspect → partial-build → fix loops.
22
+ 4. **One final screenshot only.** Do not screenshot individual sections or tables during building. Screenshot the completed doc frame once at the end.
23
+ 5. **Fetch only the sections that contain variant/token data.** If `get_design_context` on the top-level node is too large, fetch only the sections that carry structured data (Variations, Props & Tokens). Skip fetching sections that are plain text (Introduction, Usage Guidelines) — write those from what you can infer.
24
+
25
+ ---
26
+
27
+ ## Step 0 — Prerequisites Check
28
+
29
+ Call `mcp__claude_ai_Figma__whoami`. If it fails or returns an auth error:
30
+
31
+ > **Setup required before this skill can run:**
32
+ >
33
+ > 1. Open the **Claude desktop app**
34
+ > 2. Go to **Settings → Integrations** (or Extensions)
35
+ > 3. Find **Figma** and click **Enable**
36
+ > 4. **Quit and relaunch** Claude Code — the MCP won't load until you restart
37
+ > 5. Then re-run `/document-component` with your Figma URL
38
+ >
39
+ > If you've already done this and it still fails, run `claude mcp add --transport http figma https://mcp.figma.com/mcp --scope user` in your terminal, then restart.
40
+
41
+ Stop here — do not proceed until Figma MCP is confirmed working.
42
+
43
+ ---
44
+
45
+ ## Step 1 — Parse the Figma URL
46
+
47
+ Extract `fileKey` and `nodeId` from the URL:
48
+ - `figma.com/design/:fileKey/:name?node-id=:int-:int` → nodeId = `int:int` (replace `-` with `:`)
49
+ - If no URL is provided, ask for one before proceeding.
50
+
51
+ ---
52
+
53
+ ## Step 2 — Fetch the Design Context
54
+
55
+ Call `get_design_context` with **`excludeScreenshot: true`** on the top-level node. If the result is too large:
56
+ 1. Read the saved output file
57
+ 2. Extract all top-level section frame IDs using: `<frame id="..." name="Container" x="32" ...>`
58
+ 3. Call `get_design_context` (with `excludeScreenshot: true`) **in parallel** on Variations and Props & Tokens sections only — these carry the structured data you need. Infer Introduction and Usage Guidelines from the component name and token data.
59
+
60
+ Typical sections:
61
+ - Variations (add actual component snapshots under each variant — see Step 6)
62
+ - Props & Tokens
63
+ - Usage Guidelines — add Do/Don't samples wherever possible
64
+ - Platform — only if min/max width data is explicitly available in the Figma component
65
+
66
+ Parse React/Tailwind code for design token values (color hex, spacing, radius, typography).
67
+ **Do not call `get_screenshot` during this step.** A single screenshot at the end (Step 6) is sufficient.
68
+
69
+ ---
70
+
71
+ ## Step 3 — Write the Markdown File
72
+
73
+ Save as `[component-name].md` in the current directory. Follow this exact structure — no placeholders:
74
+
75
+ ```markdown
76
+ # [Component Name] — [Design System Name]
77
+
78
+ > **Source:** [Design System / Documentation](figma-url) · [company].com
79
+
80
+ ---
81
+
82
+ ## Introduction
83
+
84
+ [One paragraph: what it is, what it does, types/sizes/variants/special modes.]
85
+
86
+ ---
87
+
88
+ ## Anatomy
89
+
90
+ [Describe each structural part — table only. NO ASCII diagrams.]
91
+
92
+ | Part | Description |
93
+ |---|---|
94
+ | **[part]** | [description] |
95
+
96
+ ---
97
+
98
+ ## Variations
99
+
100
+ ### [Category — e.g. Type]
101
+
102
+ [One-line description of what this variation controls.]
103
+
104
+ | Variant | Visual Style | When to use |
105
+ |---|---|---|
106
+ | **[Name]** | [appearance] | [context] |
107
+
108
+ [Component snapshots are embedded in the Figma doc — not in the markdown]
109
+
110
+ ---
111
+
112
+ ### States
113
+
114
+ | State | Description |
115
+ |---|---|
116
+ | **Default** | Resting state |
117
+ | **Hover** | Pointer over element |
118
+ | **Focus** | Keyboard / programmatic focus |
119
+ | **Pressed** | Actively clicked or tapped |
120
+ | **Disabled** | Non-interactive |
121
+
122
+ ---
123
+
124
+ ## Props & Tokens
125
+
126
+ ### Props
127
+
128
+ | Prop Name | Required | Type | Default | Description |
129
+ |---|---|---|---|---|
130
+ | `[prop]` | ✱ / — | `[type]` | `[default]` | [description] |
131
+
132
+ > ✱ = required
133
+
134
+ ---
135
+
136
+ ### Design Tokens — [Variant]
137
+
138
+ > **Token rule:** Always use token names in every applicable column. Raw values (hex, px, numbers) go alongside the token as a reference — never as the only value. For example: `--sds-size-14` with `14px` in the Value column; `var(--sds-weight-medium) — 500` in typography.
139
+
140
+ | Property | Token Name | Value |
141
+ |---|---|---|
142
+ | [property] | `--[token]` | `[value]` |
143
+
144
+ ---
145
+
146
+ ### Size Tokens
147
+
148
+ | Size | Height | H. Padding | V. Padding | Min Width |
149
+ |---|---|---|---|---|
150
+ | Large | `[px]` | `--[token]` ([px]) | `[px]` | `[px]` |
151
+
152
+ ---
153
+
154
+ ### Typography
155
+
156
+ > **Token rule:** Family, Size, and Weight columns MUST use token names with raw values as reference (e.g. `var(--sds-family-web-font) — DM Sans`). Never output raw values alone. Line Height has no token — raw value only is fine there.
157
+
158
+ | Type | Style Token | Family | Size | Weight | Line Height |
159
+ |---|---|---|---|---|---|
160
+ | [type] | `[token]` | `var(--[family]) — [name]` | `var(--[size]) — [px]` | `var(--[weight]) — [value]` | [px] |
161
+
162
+ ---
163
+
164
+ ## Usage Guidelines
165
+
166
+ ### When to use
167
+
168
+ - [bullet]
169
+
170
+ ---
171
+
172
+ ### [Topic]
173
+
174
+ [One-line rule.]
175
+
176
+ **✓ Do**
177
+ > [Guidance with example.]
178
+
179
+ **✕ Don't**
180
+ > [What to avoid and why.]
181
+
182
+ ---
183
+
184
+ ## Platform
185
+
186
+ ### Desktop
187
+ > [Context]
188
+ - [rule]
189
+
190
+ ### Tablet
191
+ > [Context]
192
+ - [rule]
193
+
194
+ ### Mobile
195
+ > [Context]
196
+ - [rule]
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Step 4 — Generate the HTML Preview File
202
+
203
+ After writing the `.md` file, generate a self-contained `[component-name].html` file in the same directory.
204
+
205
+ ### HTML requirements
206
+
207
+ - Font: primary font from the design system (infer from Figma file; default to Inter if unknown)
208
+ - Color scheme: white background, `#1b1b1b` text, `#2563eb` accent
209
+ - Sticky left sidebar navigation linking to each `##` section
210
+ - All markdown tables as styled `<table>` elements
211
+ - `✓ Do` blocks: green card (`#f0faf4` bg, `#34a853` border)
212
+ - `✕ Don't` blocks: red card (`#fff4f4` bg, `#ea4335` border)
213
+ - Inline code / code blocks: monospace, `#f5f5f5` background
214
+ - Fixed header: component name + design system name + **"Push to Figma →"** button (`id=push-to-figma`)
215
+ - Push button: `alert('Ready to push to Figma. Run /document-component push in Claude Code.')`
216
+ - Section `##` headings get anchor `id` for sidebar deep-linking
217
+ - Responsive: sidebar collapses to top nav on narrow viewports
218
+
219
+ After writing, open:
220
+ ```bash
221
+ open [component-name].html
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Step 5 — Ask to Push to Figma
227
+
228
+ > "Documentation is ready and open in your browser. Would you like to push it into the Figma design file?"
229
+
230
+ If yes → Step 6. If no → stop.
231
+
232
+ ---
233
+
234
+ ## Step 6 — Push Documentation into the Figma Design File
235
+
236
+ Build the full documentation as real Figma text/frame nodes using `use_figma` (Plugin API). Place the frame next to the component on the same page.
237
+
238
+ > **Two-call rule:** Use exactly two `use_figma` calls to build the doc frame — no more.
239
+ > - **Call 1 (inspect):** Get page names, component set node IDs, variant IDs, and any existing doc frame IDs. Return them all.
240
+ > - **Call 2 (build):** Build the entire documentation frame in one script using the IDs from Call 1.
241
+ > - Do NOT use a third call to fix or patch. Plan the full structure before Call 2 so it runs clean.
242
+ > - After Call 2, take ONE `get_screenshot` of the completed frame to verify. That is the only screenshot in the entire flow.
243
+
244
+ > **What does NOT work — do not attempt:**
245
+ > - `upload_assets` + curl → image hash returned does NOT render as fills in Plugin API context
246
+ > - Passing base64 image data in `use_figma` code string — 50,000 char limit makes full screenshots impossible
247
+ > - ASCII text diagrams as substitutes for actual component visuals — never add these
248
+ > - `mainComponent.createInstance()` for cloning configured instances — creates fresh defaults with placeholder text, not the actual content
249
+ > - `node.resize(w, h)` to scale instances — only shrinks the bounding box, clips content; text and child elements remain at original size
250
+ > - `node.rescale(factor)` on INSTANCE nodes — same problem; only resizes the bounding box, does not scale content
251
+ > - Setting `relativeTransform` before `appendChild` — resets to 1.0 when node moves between parents
252
+
253
+ ### Async pattern — CRITICAL
254
+
255
+ **Always use top-level `await`** in `use_figma` scripts. Wrapping code in `async function main() { ... }; main()` silently drops all async results — the tool does not await the returned Promise.
256
+
257
+ ```javascript
258
+ // CORRECT — top-level await
259
+ await figma.loadFontAsync({family: 'Inter', style: 'Regular'});
260
+ const bytes = await inst.exportAsync({...});
261
+
262
+ // WRONG — main() returns a Promise the tool never awaits
263
+ async function main() { ... }
264
+ main(); // async results silently dropped
265
+ ```
266
+
267
+ ### Pre-flight (always run first)
268
+
269
+ ```javascript
270
+ // 1. Load all fonts — Inter for the doc frame, DM Sans for Cashmere component instances.
271
+ // CRITICAL: component instances use DM Sans; createInstance() throws "unloaded font" at
272
+ // appendChild if these are missing — even if you never write DM Sans text yourself.
273
+ await Promise.all([
274
+ figma.loadFontAsync({family: 'Inter', style: 'Regular'}),
275
+ figma.loadFontAsync({family: 'Inter', style: 'Medium'}),
276
+ figma.loadFontAsync({family: 'Inter', style: 'Semi Bold'}),
277
+ figma.loadFontAsync({family: 'Inter', style: 'Bold'}),
278
+ figma.loadFontAsync({family: 'DM Sans', style: 'Regular'}),
279
+ figma.loadFontAsync({family: 'DM Sans', style: 'Medium'}),
280
+ figma.loadFontAsync({family: 'DM Sans', style: 'Bold'}),
281
+ ]);
282
+
283
+ // 2. Switch to the correct page — find by the page name from the Figma URL
284
+ const targetPage = figma.root.children.find(p => p.name === 'EXACT PAGE NAME FROM URL');
285
+ // If unsure of the name, list all pages first:
286
+ // figma.root.children.map(p => p.name)
287
+ await figma.setCurrentPageAsync(targetPage);
288
+
289
+ // 3. Remove existing doc frame (idempotent safety)
290
+ targetPage.children
291
+ .filter(n => n.name === '[Component] — Documentation')
292
+ .forEach(n => n.remove());
293
+ ```
294
+
295
+ ### Frame structure
296
+
297
+ Build one top-level VERTICAL auto-layout frame (1400px wide):
298
+
299
+ ```javascript
300
+ const doc = figma.createFrame();
301
+ doc.name = '[Component] — Documentation';
302
+ doc.layoutMode = 'VERTICAL';
303
+ doc.primaryAxisSizingMode = 'AUTO';
304
+ doc.counterAxisSizingMode = 'FIXED';
305
+ doc.resize(1400, 100);
306
+ doc.clipsContent = false;
307
+ doc.fills = [{type:'SOLID', color:{r:1,g:1,b:1}}];
308
+ ```
309
+
310
+ Sections inside (each a VERTICAL auto-layout child at 1400px fixed width):
311
+ 1. **Header** — dark bg, component name + design system name
312
+ 2. **Introduction** — text block
313
+ 3. **Anatomy** — table only (no ASCII)
314
+ 4. **Variations** — table + component snapshots (see below)
315
+ 5. **Props & Tokens** — props table, tokens table, spacing table, size table, typography table
316
+ 6. **Usage Guidelines** — when to use / when not to use bullets + Do/Don't card pairs
317
+ 7. **Accessibility** — keyboard table, ARIA table, focus text, screen reader text
318
+ 8. **Related Components** — table
319
+
320
+ ### clipsContent rule
321
+
322
+ **Set `clipsContent = false` on every section frame and the top-level doc frame.**
323
+
324
+ The exception: preview wrapper frames for component snapshots may use `clipsContent = false` too — since you're using `exportAsync` (see below), there is no overflow to contain.
325
+
326
+ ```javascript
327
+ function noClip(node) {
328
+ if ('clipsContent' in node) node.clipsContent = false;
329
+ if ('children' in node) node.children.forEach(noClip);
330
+ }
331
+ noClip(doc);
332
+ ```
333
+
334
+ ### Component snapshots — how to embed actual variants
335
+
336
+ The only reliable way to show a properly scaled component instance is to export it as a PNG and embed it as an image fill. `resize()` and `rescale()` on instances only change the bounding box — they do not scale the content, causing text and layout to clip.
337
+
338
+ #### Finding the component nodes
339
+
340
+ ```javascript
341
+ // List all children of the page to find component sets
342
+ const allNodes = figma.currentPage.children.map(n =>
343
+ `${n.name} | ${n.type} | id:${n.id}`
344
+ ).join('\n');
345
+
346
+ // Find the component set by name
347
+ const compSet = figma.currentPage.findOne(n =>
348
+ n.type === 'COMPONENT_SET' && n.name.includes('YourComponentName')
349
+ );
350
+
351
+ // Its children are the individual variant components
352
+ compSet.children.forEach(c => console.log(c.name, JSON.stringify(c.variantProperties)));
353
+ ```
354
+
355
+ #### Creating variant snapshots with exportAsync
356
+
357
+ ```javascript
358
+ const SCALE = 0.45; // 0.35–0.5 works well for full-height panels/drawers
359
+ const GAP = 8;
360
+ const LABEL_H = 22;
361
+
362
+ // Find the variant component (a COMPONENT node, not COMPONENT_SET)
363
+ const variantComp = compSet.children.find(c =>
364
+ c.variantProperties && Object.values(c.variantProperties).some(v => v === 'VariantName')
365
+ );
366
+
367
+ // 1. Create instance off-canvas for export
368
+ const inst = variantComp.createInstance();
369
+ figma.currentPage.appendChild(inst);
370
+ inst.x = -99999; inst.y = -99999;
371
+
372
+ const sw = Math.round(inst.width * SCALE);
373
+ const sh = Math.round(inst.height * SCALE);
374
+
375
+ // 2. Export at scaled size — this is the ONLY way to get proper visual scale
376
+ const bytes = await inst.exportAsync({
377
+ format: 'PNG',
378
+ constraint: {type: 'SCALE', value: SCALE}
379
+ });
380
+ inst.remove();
381
+
382
+ // 3. Embed as image fill on a rectangle
383
+ const image = figma.createImage(bytes);
384
+ const rect = figma.createRectangle();
385
+ rect.name = 'Preview — VariantName';
386
+ rect.resize(sw, sh);
387
+ rect.fills = [{type: 'IMAGE', imageHash: image.hash, scaleMode: 'FILL'}];
388
+ rect.cornerRadius = 4;
389
+
390
+ // 4. Wrapper frame with label below
391
+ const wrapper = figma.createFrame();
392
+ wrapper.name = 'Component Preview — VariantName';
393
+ wrapper.layoutMode = 'NONE';
394
+ wrapper.resize(sw, sh + GAP + LABEL_H);
395
+ wrapper.fills = [];
396
+ wrapper.clipsContent = false;
397
+
398
+ wrapper.appendChild(rect);
399
+ rect.x = 0; rect.y = 0;
400
+
401
+ const lbl = figma.createText();
402
+ lbl.characters = 'VariantName';
403
+ lbl.fontSize = 12;
404
+ lbl.fontName = {family: 'Inter', style: 'Regular'};
405
+ lbl.fills = [{type: 'SOLID', color: {r: 0.6, g: 0.6, b: 0.6}}];
406
+ wrapper.appendChild(lbl);
407
+ lbl.x = 0; lbl.y = sh + GAP;
408
+ ```
409
+
410
+ #### Laying out multiple variant previews side by side
411
+
412
+ ```javascript
413
+ const PADDING = 24;
414
+ const BETWEEN_GAP = 16;
415
+
416
+ // Build all wrappers (await each exportAsync separately — top-level await)
417
+ const bytes1 = await inst1.exportAsync({format: 'PNG', constraint: {type: 'SCALE', value: SCALE}});
418
+ // ... build wrap1 ...
419
+
420
+ const bytes2 = await inst2.exportAsync({format: 'PNG', constraint: {type: 'SCALE', value: SCALE}});
421
+ // ... build wrap2 ...
422
+
423
+ // Add to preview row with explicit positioning
424
+ previewRow.appendChild(wrap1);
425
+ wrap1.x = PADDING; wrap1.y = PADDING;
426
+
427
+ previewRow.appendChild(wrap2);
428
+ wrap2.x = PADDING + wrap1.width + BETWEEN_GAP; wrap2.y = PADDING;
429
+
430
+ const totalW = PADDING + wrap1.width + BETWEEN_GAP + wrap2.width + PADDING;
431
+ const totalH = PADDING + Math.max(wrap1.height, wrap2.height) + PADDING;
432
+ previewRow.resize(totalW, totalH);
433
+ previewRow.clipsContent = false;
434
+ ```
435
+
436
+ #### Cloning configured sub-item instances (for individual states/types within a component)
437
+
438
+ Use `.clone()` — NOT `mainComponent.createInstance()`. Clone preserves actual overrides (labels, icons, toggle states):
439
+
440
+ ```javascript
441
+ const configuredItem = figma.getNodeById('<INSTANCE_NODE_ID>'); // an INSTANCE node
442
+ const clone = configuredItem.clone();
443
+ myContainer.appendChild(clone);
444
+ ```
445
+
446
+ #### Inserting snapshots at the right position inside a section
447
+
448
+ Sections use `insertChild(index, child)`. Track index shifts — each insertion bumps subsequent indices by 1:
449
+
450
+ ```javascript
451
+ let tableIdx = varSection.children.findIndex(c => c.name === 'table');
452
+ varSection.insertChild(tableIdx + 1, snapshotRow);
453
+
454
+ // For the SECOND table (index shifted +1 after first insertion)
455
+ let secondTableIdx = varSection.children.findIndex((c, i) => c.name === 'table' && i > tableIdx + 1);
456
+ varSection.insertChild(secondTableIdx + 1, secondSnapshotRow);
457
+ ```
458
+
459
+ ### Positioning the doc frame
460
+
461
+ Place the doc frame to the right of the component with a gap:
462
+
463
+ ```javascript
464
+ // Find the bounding box of the component on the page
465
+ const comp = figma.currentPage.findOne(n =>
466
+ (n.type === 'COMPONENT_SET' || n.type === 'FRAME') && n.name.includes('YourComponentName')
467
+ );
468
+ doc.x = comp.x + comp.width + 120;
469
+ doc.y = comp.y;
470
+ targetPage.appendChild(doc);
471
+ figma.viewport.scrollAndZoomIntoView([doc]);
472
+ ```
473
+
474
+ ---
475
+
476
+ ## Quality Checklist
477
+
478
+ Before finishing, verify:
479
+
480
+ - [ ] `.md` file has no placeholder text and no ASCII diagrams
481
+ - [ ] Every prop has type, default, and description
482
+ - [ ] All design token names start with `--` and have hex/px values
483
+ - [ ] Each usage guideline has both a ✓ Do and ✕ Don't
484
+ - [ ] `.html` file opens correctly in the browser
485
+ - [ ] HTML sidebar links to all `##` sections
486
+ - [ ] Do/Don't cards render in correct green/red colors
487
+ - [ ] "Push to Figma →" button visible in header
488
+ - [ ] Figma doc frame: no ASCII diagrams anywhere
489
+ - [ ] Figma doc frame: `clipsContent = false` on all section frames (run `noClip(doc)` at the end)
490
+ - [ ] Figma doc frame: actual component snapshots in Variations section (not placeholder text, not clipped)
491
+ - [ ] Figma doc frame: snapshots built with `exportAsync` + image fills (not `resize()`/`rescale()`)
492
+ - [ ] Figma doc frame: positioned on same page as component, to the right with 120px gap
493
+ - [ ] `figma.viewport.scrollAndZoomIntoView([doc])` called at end so user sees it
494
+ - [ ] All `use_figma` scripts use top-level `await` (not `async function main() { ... }; main()`)
495
+
496
+ ---
497
+
498
+ ## Notes
499
+
500
+ - Adapt section names to match whatever the Figma doc uses — not all components have every section
501
+ - Additional sections (Accessibility, Changelog, Figma usage) go after Platform
502
+ - Token naming pattern varies by design system — infer from the Figma file (e.g. `--sds-*`, `--ds-*`, `--color-*`); do not assume a fixed prefix
503
+ - If the node is a component (not a doc page), fetch individual variant states in parallel to extract tokens
504
+ - The HTML file is the primary review artifact — make it polished enough to share with a design team
505
+ - To find the correct page name for `setCurrentPageAsync`, list `figma.root.children.map(p => p.name)` first rather than guessing
@@ -0,0 +1,46 @@
1
+ Write clear, useful documentation for a component.
2
+
3
+ If the user hasn't specified which component, ask them which one they want to document.
4
+
5
+ Find the component in the project and write documentation that includes:
6
+
7
+ ---
8
+
9
+ **1. What it is**
10
+ One clear sentence. What does it do and when would you reach for it?
11
+
12
+ **2. When to use it**
13
+ 2–3 specific, realistic use cases.
14
+ Also note: when *not* to use it (what to use instead).
15
+
16
+ **3. Options and configuration**
17
+ List every way to configure this component. For each option:
18
+ - What it's called (in plain language, not just the prop name)
19
+ - What it does visually
20
+ - What values are available
21
+ - What the default is
22
+
23
+ Write this for a designer, not a developer. Say "Sets the button to its danger/red style" not "type: 'destructive'".
24
+
25
+ **4. Visual states**
26
+ Describe what changes visually for each state:
27
+ - Default
28
+ - Hover
29
+ - Active / pressed
30
+ - Focused (keyboard)
31
+ - Disabled
32
+ - Loading (if applicable)
33
+ - Error (if applicable)
34
+
35
+ **5. Usage examples**
36
+ 2–3 realistic examples with a sentence of context for each. When would you actually use this?
37
+
38
+ **6. Accessibility**
39
+ - Can it be used with keyboard only? How?
40
+ - What does a screen reader announce?
41
+ - Any important color contrast or touch target notes?
42
+
43
+ ---
44
+
45
+ Save the documentation as `[ComponentName].docs.md` in the same folder as the component.
46
+ After saving, confirm the file path and offer a one-line summary of what was documented.