@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 +84 -0
- package/bin/cli.js +20 -0
- package/bin/design.js +27 -0
- package/commands/document-component.md +505 -0
- package/commands/document.md +46 -0
- package/commands/figma.md +56 -0
- package/commands/handoff.md +92 -0
- package/commands/new-component.md +26 -0
- package/commands/qa.md +89 -0
- package/commands/review.md +49 -0
- package/commands/spec.md +77 -0
- package/commands/tokens.md +42 -0
- package/package.json +44 -0
- package/src/setup.js +397 -0
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.
|