figma-code-agent 1.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/README.md +133 -0
- package/bin/install.js +328 -0
- package/knowledge/README.md +62 -0
- package/knowledge/css-strategy.md +973 -0
- package/knowledge/design-to-code-assets.md +855 -0
- package/knowledge/design-to-code-layout.md +929 -0
- package/knowledge/design-to-code-semantic.md +1085 -0
- package/knowledge/design-to-code-typography.md +1003 -0
- package/knowledge/design-to-code-visual.md +1145 -0
- package/knowledge/design-tokens-variables.md +1261 -0
- package/knowledge/design-tokens.md +960 -0
- package/knowledge/figma-api-devmode.md +894 -0
- package/knowledge/figma-api-plugin.md +920 -0
- package/knowledge/figma-api-rest.md +742 -0
- package/knowledge/figma-api-variables.md +848 -0
- package/knowledge/figma-api-webhooks.md +876 -0
- package/knowledge/payload-blocks.md +1184 -0
- package/knowledge/payload-figma-mapping.md +1210 -0
- package/knowledge/payload-visual-builder.md +1004 -0
- package/knowledge/plugin-architecture.md +1176 -0
- package/knowledge/plugin-best-practices.md +1206 -0
- package/knowledge/plugin-codegen.md +1313 -0
- package/package.json +31 -0
- package/skills/README.md +103 -0
- package/skills/audit-plugin/SKILL.md +244 -0
- package/skills/build-codegen-plugin/SKILL.md +279 -0
- package/skills/build-importer/SKILL.md +320 -0
- package/skills/build-plugin/SKILL.md +199 -0
- package/skills/build-token-pipeline/SKILL.md +363 -0
- package/skills/ref-html/SKILL.md +290 -0
- package/skills/ref-layout/SKILL.md +150 -0
- package/skills/ref-payload-block/SKILL.md +415 -0
- package/skills/ref-react/SKILL.md +222 -0
- package/skills/ref-tokens/SKILL.md +347 -0
|
@@ -0,0 +1,894 @@
|
|
|
1
|
+
# Figma Dev Mode & Codegen Plugin Reference
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Authoritative reference for Figma Dev Mode, the codegen plugin system, and the Dev Resources REST API. Covers the Dev Mode inspect experience, codegen plugin manifests (with correct `editorType: ["dev"]`), the `figma.codegen` API, code generation patterns, and the Dev Resources CRUD endpoints for linking design components to code.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Reference this module when you need to:
|
|
10
|
+
|
|
11
|
+
- Build a codegen plugin that generates code in Dev Mode's Inspect panel
|
|
12
|
+
- Configure a `manifest.json` for a Dev Mode codegen plugin
|
|
13
|
+
- Implement the `figma.codegen.on('generate', ...)` callback
|
|
14
|
+
- Use the codegen preferences system (unit, select, action)
|
|
15
|
+
- Create, read, update, or delete Dev Resources via the REST API
|
|
16
|
+
- Link Figma components to code repositories or documentation
|
|
17
|
+
- Understand how Dev Mode relates to the standard Figma editing experience
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Content
|
|
22
|
+
|
|
23
|
+
### Dev Mode Overview
|
|
24
|
+
|
|
25
|
+
Dev Mode is Figma's dedicated developer experience that transforms the Inspect panel into a developer-focused workspace. It surfaces design specs, code snippets, annotations, and developer resources alongside the visual design.
|
|
26
|
+
|
|
27
|
+
#### Key Concepts
|
|
28
|
+
|
|
29
|
+
| Concept | Description |
|
|
30
|
+
|---------|-------------|
|
|
31
|
+
| **Inspect panel** | The right-side panel in Dev Mode showing properties, code, and resources for the selected node |
|
|
32
|
+
| **Code snippets** | Built-in code generation (CSS, iOS, Android) plus custom snippets from codegen plugins |
|
|
33
|
+
| **Dev Resources** | Links attached to nodes pointing to code repos, documentation, or other developer references |
|
|
34
|
+
| **Ready for dev** | A status designers can apply to frames/sections indicating they are ready for implementation |
|
|
35
|
+
| **Annotations** | Design notes and specifications attached to nodes, visible in Dev Mode |
|
|
36
|
+
| **Measurements** | Spacing and dimension overlays visible when hovering between elements |
|
|
37
|
+
|
|
38
|
+
#### Relationship to Standard Editing Mode
|
|
39
|
+
|
|
40
|
+
Dev Mode and Design Mode are separate views of the same Figma file:
|
|
41
|
+
|
|
42
|
+
- **Design Mode** — The editing environment where designers create and modify designs. Standard plugins (`editorType: ["figma"]`) run here.
|
|
43
|
+
- **Dev Mode** — The read-only inspection environment for developers. Codegen plugins (`editorType: ["dev"]`) run here. Developers can view properties, copy code, and access dev resources, but cannot modify the design.
|
|
44
|
+
|
|
45
|
+
Plugins can detect which mode they are running in:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
if (figma.editorType === 'dev') {
|
|
49
|
+
// Running in Dev Mode
|
|
50
|
+
if (figma.mode === 'codegen') {
|
|
51
|
+
// Specifically running as a codegen plugin
|
|
52
|
+
} else if (figma.mode === 'inspect') {
|
|
53
|
+
// Running as an inspect plugin
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### Codegen Plugin System
|
|
61
|
+
|
|
62
|
+
Codegen plugins extend Figma's built-in code generation with custom languages and frameworks. They appear in the Dev Mode Inspect panel's language dropdown and generate code snippets when a user selects a node.
|
|
63
|
+
|
|
64
|
+
#### How Codegen Plugins Work
|
|
65
|
+
|
|
66
|
+
1. The user opens a Figma file in Dev Mode
|
|
67
|
+
2. The user selects a codegen plugin's language from the Inspect panel dropdown
|
|
68
|
+
3. When the user selects a node, Figma fires the `generate` event
|
|
69
|
+
4. The plugin's callback receives the selected node and returns code snippets
|
|
70
|
+
5. The code appears in the Inspect panel alongside built-in CSS/iOS/Android code
|
|
71
|
+
|
|
72
|
+
> **Key difference from standard plugins:** Codegen plugins do NOT have an iframe UI by default. The generated code appears directly in the Inspect panel. However, they CAN use `figma.showUI()` for advanced scenarios like action preferences (see Preferences section below).
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Codegen Plugin Manifest
|
|
77
|
+
|
|
78
|
+
Codegen plugin manifests use `editorType: ["dev"]` with `capabilities: ["codegen"]`. This is fundamentally different from standard plugin manifests.
|
|
79
|
+
|
|
80
|
+
> **Critical:** Do NOT use `editorType: ["figma"]` or `editorType: ["figma", "figjam"]` for codegen plugins. The audit specifically identified this as a common error. Codegen plugins MUST use `editorType: ["dev"]`.
|
|
81
|
+
|
|
82
|
+
#### Minimal Codegen Manifest
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"name": "My Codegen Plugin",
|
|
87
|
+
"id": "1234567890",
|
|
88
|
+
"api": "1.0.0",
|
|
89
|
+
"main": "code.js",
|
|
90
|
+
"editorType": ["dev"],
|
|
91
|
+
"documentAccess": "dynamic-page",
|
|
92
|
+
"capabilities": ["codegen"],
|
|
93
|
+
"codegenLanguages": [
|
|
94
|
+
{ "label": "React", "value": "react" }
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Full Codegen Manifest with Preferences
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"name": "Design-to-Code Generator",
|
|
104
|
+
"id": "1234567890",
|
|
105
|
+
"api": "1.0.0",
|
|
106
|
+
"main": "code.js",
|
|
107
|
+
"editorType": ["dev"],
|
|
108
|
+
"documentAccess": "dynamic-page",
|
|
109
|
+
"capabilities": ["codegen", "vscode"],
|
|
110
|
+
"codegenLanguages": [
|
|
111
|
+
{ "label": "React", "value": "react" },
|
|
112
|
+
{ "label": "Vue", "value": "vue" },
|
|
113
|
+
{ "label": "HTML + CSS", "value": "html-css" }
|
|
114
|
+
],
|
|
115
|
+
"codegenPreferences": [
|
|
116
|
+
{
|
|
117
|
+
"itemType": "unit",
|
|
118
|
+
"propertyName": "Sizing Unit",
|
|
119
|
+
"scaledUnit": "rem",
|
|
120
|
+
"defaultScaleFactor": 16,
|
|
121
|
+
"includedLanguages": ["react", "html-css"]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"itemType": "select",
|
|
125
|
+
"propertyName": "CSS Strategy",
|
|
126
|
+
"options": [
|
|
127
|
+
{ "label": "CSS Modules", "value": "modules", "isDefault": true },
|
|
128
|
+
{ "label": "Tailwind", "value": "tailwind" },
|
|
129
|
+
{ "label": "Inline Styles", "value": "inline" }
|
|
130
|
+
],
|
|
131
|
+
"includedLanguages": ["react"]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"itemType": "action",
|
|
135
|
+
"propertyName": "Component Mapping",
|
|
136
|
+
"label": "Configure Mappings"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"networkAccess": {
|
|
140
|
+
"allowedDomains": ["https://api.example.com"],
|
|
141
|
+
"reasoning": "Fetch component mappings from design system server"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Manifest Field Reference (Codegen-Specific)
|
|
147
|
+
|
|
148
|
+
| Field | Type | Required | Description |
|
|
149
|
+
|-------|------|:--------:|-------------|
|
|
150
|
+
| `editorType` | string[] | Yes | Must be `["dev"]` for codegen plugins |
|
|
151
|
+
| `capabilities` | string[] | Yes | Must include `"codegen"`. Can also include `"vscode"` for VS Code extension integration and `"inspect"` for inspect panel plugins |
|
|
152
|
+
| `codegenLanguages` | object[] | Yes | Array of `{ label: string, value: string }` defining supported languages |
|
|
153
|
+
| `codegenPreferences` | object[] | No | Preference items for customizing code output (see Preferences section) |
|
|
154
|
+
|
|
155
|
+
#### codegenLanguages
|
|
156
|
+
|
|
157
|
+
Each entry defines a language/framework option in the Dev Mode dropdown:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
"codegenLanguages": [
|
|
161
|
+
{ "label": "React (TSX)", "value": "react-tsx" },
|
|
162
|
+
{ "label": "Vue SFC", "value": "vue-sfc" },
|
|
163
|
+
{ "label": "HTML + CSS", "value": "html-css" },
|
|
164
|
+
{ "label": "Tailwind", "value": "tailwind" },
|
|
165
|
+
{ "label": "SwiftUI", "value": "swiftui" }
|
|
166
|
+
]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- `label` — Display name shown in the dropdown
|
|
170
|
+
- `value` — Identifier passed to the generate callback
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### Codegen API
|
|
175
|
+
|
|
176
|
+
The `figma.codegen` object provides the API for code generation plugins.
|
|
177
|
+
|
|
178
|
+
#### The Generate Callback
|
|
179
|
+
|
|
180
|
+
The core of every codegen plugin is the `generate` event handler:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
figma.codegen.on('generate', (event: CodegenEvent): CodegenResult[] => {
|
|
184
|
+
const { node, language } = event;
|
|
185
|
+
|
|
186
|
+
// Generate code based on the selected node and language
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
title: 'JSX',
|
|
190
|
+
code: generateJSX(node),
|
|
191
|
+
language: 'TYPESCRIPT'
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
title: 'Styles',
|
|
195
|
+
code: generateCSS(node),
|
|
196
|
+
language: 'CSS'
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### CodegenEvent
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
interface CodegenEvent {
|
|
206
|
+
/** The currently selected node in Dev Mode */
|
|
207
|
+
node: SceneNode;
|
|
208
|
+
|
|
209
|
+
/** The language value from codegenLanguages in manifest */
|
|
210
|
+
language: string;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### CodegenResult
|
|
215
|
+
|
|
216
|
+
Each result creates a titled code section in the Inspect panel:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
interface CodegenResult {
|
|
220
|
+
/** Section title displayed above the code block */
|
|
221
|
+
title: string;
|
|
222
|
+
|
|
223
|
+
/** The generated code string */
|
|
224
|
+
code: string;
|
|
225
|
+
|
|
226
|
+
/** Syntax highlighting language */
|
|
227
|
+
language: 'BASH' | 'CPP' | 'CSS' | 'GO' | 'GRAPHQL' | 'HTML' | 'JAVA'
|
|
228
|
+
| 'JAVASCRIPT' | 'JSON' | 'KOTLIN' | 'PLAINTEXT' | 'PYTHON' | 'RUBY'
|
|
229
|
+
| 'RUST' | 'SASS' | 'SCSS' | 'SWIFT' | 'TYPESCRIPT' | 'XML';
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Multiple results are rendered as separate collapsible sections in the Inspect panel. This allows a single generate callback to output HTML, CSS, and JavaScript as separate sections.
|
|
234
|
+
|
|
235
|
+
#### Async Generate Callback
|
|
236
|
+
|
|
237
|
+
The generate callback can return a Promise for async operations:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
figma.codegen.on('generate', async (event: CodegenEvent): Promise<CodegenResult[]> => {
|
|
241
|
+
const { node, language } = event;
|
|
242
|
+
|
|
243
|
+
// Async operations (e.g., loading fonts, resolving variables)
|
|
244
|
+
if (node.type === 'TEXT') {
|
|
245
|
+
await figma.loadFontAsync(node.fontName as FontName);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return [{
|
|
249
|
+
title: 'Component',
|
|
250
|
+
code: await generateComponent(node, language),
|
|
251
|
+
language: 'TYPESCRIPT'
|
|
252
|
+
}];
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
> **Important:** The generate callback has a **3-second timeout**. If the callback does not resolve within 3 seconds, Figma will cancel it. Keep code generation fast.
|
|
257
|
+
|
|
258
|
+
#### Preferences API
|
|
259
|
+
|
|
260
|
+
Access current preference values in the generate callback:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
figma.codegen.on('generate', (event) => {
|
|
264
|
+
const prefs = figma.codegen.preferences;
|
|
265
|
+
|
|
266
|
+
// Unit preference: { unit: string, scaleFactor: number }
|
|
267
|
+
const sizingUnit = prefs.unit; // e.g., "rem"
|
|
268
|
+
const scaleFactor = prefs.scaleFactor; // e.g., 16
|
|
269
|
+
|
|
270
|
+
// Select preference: check current value
|
|
271
|
+
const cssStrategy = prefs['CSS Strategy']; // e.g., "modules"
|
|
272
|
+
|
|
273
|
+
// Use preferences in code generation
|
|
274
|
+
const pxValue = node.width;
|
|
275
|
+
const remValue = pxValue / scaleFactor;
|
|
276
|
+
const sizeStr = sizingUnit === 'rem' ? `${remValue}rem` : `${pxValue}px`;
|
|
277
|
+
|
|
278
|
+
return [{ title: 'Styles', code: `width: ${sizeStr};`, language: 'CSS' }];
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Preference Change Events
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
// Listen for action preference changes (when user closes action UI)
|
|
286
|
+
figma.codegen.on('preferenceschange', (event) => {
|
|
287
|
+
// Preferences have been updated, refresh will happen automatically
|
|
288
|
+
// unless using action preferences that require manual refresh
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Manual Refresh
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
// Force the Inspect panel to re-run the generate callback
|
|
296
|
+
figma.codegen.refresh();
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### Codegen Preference Types
|
|
302
|
+
|
|
303
|
+
#### Unit Preferences
|
|
304
|
+
|
|
305
|
+
Allow users to choose a CSS unit and scale factor (e.g., px to rem conversion).
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"itemType": "unit",
|
|
310
|
+
"propertyName": "Size Unit",
|
|
311
|
+
"scaledUnit": "rem",
|
|
312
|
+
"defaultScaleFactor": 16,
|
|
313
|
+
"includedLanguages": ["react", "html-css"]
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
- Defined once per language set
|
|
318
|
+
- Accessible via `figma.codegen.preferences.unit` and `.scaleFactor`
|
|
319
|
+
- Changing the preference automatically triggers the generate callback
|
|
320
|
+
|
|
321
|
+
#### Select Preferences
|
|
322
|
+
|
|
323
|
+
Multiple-choice dropdowns for formatting options.
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"itemType": "select",
|
|
328
|
+
"propertyName": "Style Format",
|
|
329
|
+
"options": [
|
|
330
|
+
{ "label": "CSS Modules", "value": "modules", "isDefault": true },
|
|
331
|
+
{ "label": "Tailwind CSS", "value": "tailwind" },
|
|
332
|
+
{ "label": "Styled Components", "value": "styled" }
|
|
333
|
+
],
|
|
334
|
+
"includedLanguages": ["react"]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
- Accessible via `figma.codegen.preferences['Style Format']`
|
|
339
|
+
- Changing the selection automatically triggers the generate callback
|
|
340
|
+
|
|
341
|
+
#### Action Preferences
|
|
342
|
+
|
|
343
|
+
Open a custom UI for complex configuration (e.g., component mapping tables).
|
|
344
|
+
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"itemType": "action",
|
|
348
|
+
"propertyName": "Component Config",
|
|
349
|
+
"label": "Configure Components"
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
- Clicking the action button opens the UI via `figma.showUI()`
|
|
354
|
+
- Data should be stored via `figma.clientStorage`
|
|
355
|
+
- Requires manual `figma.codegen.refresh()` after changes
|
|
356
|
+
- The UI must call `figma.closePlugin()` or be hidden when done
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
// Handle action preference
|
|
360
|
+
figma.codegen.on('preferenceschange', async (event) => {
|
|
361
|
+
if (event.propertyName === 'Component Config') {
|
|
362
|
+
// Show UI for configuration
|
|
363
|
+
figma.showUI(__html__, { width: 500, height: 400 });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// When UI sends updated config
|
|
368
|
+
figma.ui.on('message', async (msg) => {
|
|
369
|
+
if (msg.type === 'CONFIG_SAVED') {
|
|
370
|
+
await figma.clientStorage.setAsync('componentConfig', msg.config);
|
|
371
|
+
figma.ui.hide();
|
|
372
|
+
figma.codegen.refresh(); // Re-run generate with new config
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### Codegen Plugin Patterns
|
|
380
|
+
|
|
381
|
+
#### Basic React Code Generation
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
figma.codegen.on('generate', (event) => {
|
|
385
|
+
const { node, language } = event;
|
|
386
|
+
|
|
387
|
+
if (language !== 'react') return [];
|
|
388
|
+
|
|
389
|
+
const componentName = toPascalCase(node.name);
|
|
390
|
+
const styles = generateStyles(node);
|
|
391
|
+
const jsx = generateJSX(node);
|
|
392
|
+
|
|
393
|
+
return [
|
|
394
|
+
{
|
|
395
|
+
title: `${componentName}.tsx`,
|
|
396
|
+
language: 'TYPESCRIPT',
|
|
397
|
+
code: [
|
|
398
|
+
`import styles from './${componentName}.module.css';`,
|
|
399
|
+
'',
|
|
400
|
+
`export function ${componentName}() {`,
|
|
401
|
+
` return (`,
|
|
402
|
+
` ${jsx}`,
|
|
403
|
+
` );`,
|
|
404
|
+
`}`,
|
|
405
|
+
].join('\n')
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
title: `${componentName}.module.css`,
|
|
409
|
+
language: 'CSS',
|
|
410
|
+
code: styles
|
|
411
|
+
}
|
|
412
|
+
];
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
function toPascalCase(str: string): string {
|
|
416
|
+
return str
|
|
417
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
|
|
418
|
+
.replace(/^./, (c) => c.toUpperCase());
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### Multi-Language Support
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
figma.codegen.on('generate', (event) => {
|
|
426
|
+
const { node, language } = event;
|
|
427
|
+
|
|
428
|
+
switch (language) {
|
|
429
|
+
case 'react':
|
|
430
|
+
return generateReact(node);
|
|
431
|
+
case 'vue':
|
|
432
|
+
return generateVue(node);
|
|
433
|
+
case 'html-css':
|
|
434
|
+
return generateHTML(node);
|
|
435
|
+
case 'tailwind':
|
|
436
|
+
return generateTailwind(node);
|
|
437
|
+
default:
|
|
438
|
+
return [{
|
|
439
|
+
title: 'Unsupported',
|
|
440
|
+
code: `// Language "${language}" not yet supported`,
|
|
441
|
+
language: 'PLAINTEXT'
|
|
442
|
+
}];
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### Reading Layout Properties for CSS
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
function generateLayoutCSS(node: SceneNode): string {
|
|
451
|
+
const rules: string[] = [];
|
|
452
|
+
|
|
453
|
+
if ('layoutMode' in node && node.layoutMode !== 'NONE') {
|
|
454
|
+
rules.push('display: flex;');
|
|
455
|
+
rules.push(`flex-direction: ${node.layoutMode === 'HORIZONTAL' ? 'row' : 'column'};`);
|
|
456
|
+
|
|
457
|
+
if (node.itemSpacing > 0) {
|
|
458
|
+
rules.push(`gap: ${node.itemSpacing}px;`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (node.layoutWrap === 'WRAP') {
|
|
462
|
+
rules.push('flex-wrap: wrap;');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Primary axis alignment (justify-content)
|
|
466
|
+
const justifyMap: Record<string, string> = {
|
|
467
|
+
'MIN': 'flex-start',
|
|
468
|
+
'CENTER': 'center',
|
|
469
|
+
'MAX': 'flex-end',
|
|
470
|
+
'SPACE_BETWEEN': 'space-between',
|
|
471
|
+
};
|
|
472
|
+
rules.push(`justify-content: ${justifyMap[node.primaryAxisAlignItems] || 'flex-start'};`);
|
|
473
|
+
|
|
474
|
+
// Cross axis alignment (align-items)
|
|
475
|
+
const alignMap: Record<string, string> = {
|
|
476
|
+
'MIN': 'flex-start',
|
|
477
|
+
'CENTER': 'center',
|
|
478
|
+
'MAX': 'flex-end',
|
|
479
|
+
'BASELINE': 'baseline',
|
|
480
|
+
};
|
|
481
|
+
rules.push(`align-items: ${alignMap[node.counterAxisAlignItems] || 'flex-start'};`);
|
|
482
|
+
|
|
483
|
+
// Padding
|
|
484
|
+
const { paddingTop, paddingRight, paddingBottom, paddingLeft } = node;
|
|
485
|
+
if (paddingTop || paddingRight || paddingBottom || paddingLeft) {
|
|
486
|
+
rules.push(`padding: ${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px;`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Sizing
|
|
491
|
+
if ('layoutSizingHorizontal' in node) {
|
|
492
|
+
if (node.layoutSizingHorizontal === 'FIXED') {
|
|
493
|
+
rules.push(`width: ${node.width}px;`);
|
|
494
|
+
} else if (node.layoutSizingHorizontal === 'FILL') {
|
|
495
|
+
rules.push('flex: 1 0 0;');
|
|
496
|
+
rules.push('width: 100%;');
|
|
497
|
+
}
|
|
498
|
+
// HUG: width is auto (no explicit CSS needed)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if ('layoutSizingVertical' in node) {
|
|
502
|
+
if (node.layoutSizingVertical === 'FIXED') {
|
|
503
|
+
rules.push(`height: ${node.height}px;`);
|
|
504
|
+
} else if (node.layoutSizingVertical === 'FILL') {
|
|
505
|
+
rules.push('flex: 1 0 0;');
|
|
506
|
+
rules.push('height: 100%;');
|
|
507
|
+
}
|
|
508
|
+
// HUG: height is auto (no explicit CSS needed)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return rules.join('\n');
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
#### Handling Component Instances vs Definitions
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
figma.codegen.on('generate', async ({ node, language }) => {
|
|
519
|
+
if (node.type === 'INSTANCE') {
|
|
520
|
+
const mainComponent = await node.getMainComponentAsync();
|
|
521
|
+
if (mainComponent) {
|
|
522
|
+
const componentName = toPascalCase(mainComponent.name);
|
|
523
|
+
const props = extractOverrides(node, mainComponent);
|
|
524
|
+
|
|
525
|
+
return [{
|
|
526
|
+
title: 'Usage',
|
|
527
|
+
language: 'TYPESCRIPT',
|
|
528
|
+
code: `<${componentName} ${formatProps(props)} />`
|
|
529
|
+
}];
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (node.type === 'COMPONENT') {
|
|
534
|
+
// Generate the component definition
|
|
535
|
+
return generateComponentDefinition(node, language);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// For other nodes, generate inline code
|
|
539
|
+
return generateInlineElement(node, language);
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### Using iframe for Complex Processing in Codegen
|
|
544
|
+
|
|
545
|
+
Although codegen plugins typically do not have a visible UI, they can use a hidden iframe for operations that need browser APIs:
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
let nextMessageIndex = 1;
|
|
549
|
+
const resolvers: Record<number, (result: CodegenResult[]) => void> = {};
|
|
550
|
+
|
|
551
|
+
// Show hidden UI for processing
|
|
552
|
+
figma.showUI('<script>/* processing code */</script>', { visible: false });
|
|
553
|
+
|
|
554
|
+
figma.ui.on('message', (msg) => {
|
|
555
|
+
if (msg.type === 'CODEGEN_RESULT' && resolvers[msg.messageIdx]) {
|
|
556
|
+
resolvers[msg.messageIdx](msg.results);
|
|
557
|
+
delete resolvers[msg.messageIdx];
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
figma.codegen.on('generate', async ({ node, language }) => {
|
|
562
|
+
const messageIdx = nextMessageIndex++;
|
|
563
|
+
|
|
564
|
+
return new Promise<CodegenResult[]>((resolve) => {
|
|
565
|
+
resolvers[messageIdx] = resolve;
|
|
566
|
+
figma.ui.postMessage({
|
|
567
|
+
type: 'GENERATE',
|
|
568
|
+
messageIdx,
|
|
569
|
+
nodeData: serializeNode(node),
|
|
570
|
+
language
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
### Dev Resources REST API
|
|
579
|
+
|
|
580
|
+
Dev Resources are links attached to Figma nodes that point to external developer references (code repos, documentation, Storybook, etc.). They are visible in the Dev Mode Inspect panel.
|
|
581
|
+
|
|
582
|
+
#### Authentication
|
|
583
|
+
|
|
584
|
+
All Dev Resources endpoints require a Figma API token (PAT or OAuth) with the appropriate scope:
|
|
585
|
+
|
|
586
|
+
| Operation | Scope Required |
|
|
587
|
+
|-----------|---------------|
|
|
588
|
+
| Read | `file_dev_resources:read` |
|
|
589
|
+
| Create / Update / Delete | `file_dev_resources:write` |
|
|
590
|
+
|
|
591
|
+
All Dev Resources endpoints are **Rate limit tier: Tier 2**.
|
|
592
|
+
|
|
593
|
+
#### GET /v1/files/:file_key/dev_resources
|
|
594
|
+
|
|
595
|
+
Retrieve dev resources from a file.
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
curl -H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
|
|
599
|
+
"https://api.figma.com/v1/files/FILE_KEY/dev_resources"
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Optional query parameters:**
|
|
603
|
+
|
|
604
|
+
| Parameter | Type | Description |
|
|
605
|
+
|-----------|------|-------------|
|
|
606
|
+
| `node_ids` | string | Comma-separated node IDs to filter results |
|
|
607
|
+
|
|
608
|
+
**Response:**
|
|
609
|
+
|
|
610
|
+
```ts
|
|
611
|
+
interface GetDevResourcesResponse {
|
|
612
|
+
dev_resources: DevResource[];
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
interface DevResource {
|
|
616
|
+
id: string;
|
|
617
|
+
name: string;
|
|
618
|
+
url: string;
|
|
619
|
+
file_key: string;
|
|
620
|
+
node_id: string;
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
#### POST /v1/dev_resources
|
|
625
|
+
|
|
626
|
+
Create dev resources in bulk (can span multiple files).
|
|
627
|
+
|
|
628
|
+
> **Critical:** The request body uses `dev_resources` (plural array), NOT a singular `dev_resource` object. This was specifically flagged in the audit as a common error.
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
curl -X POST \
|
|
632
|
+
-H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
|
|
633
|
+
-H "Content-Type: application/json" \
|
|
634
|
+
-d '{
|
|
635
|
+
"dev_resources": [
|
|
636
|
+
{
|
|
637
|
+
"name": "React Component",
|
|
638
|
+
"url": "https://github.com/org/repo/blob/main/src/Button.tsx",
|
|
639
|
+
"file_key": "FILE_KEY",
|
|
640
|
+
"node_id": "1:2"
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
"name": "Storybook",
|
|
644
|
+
"url": "https://storybook.example.com/?path=/story/button",
|
|
645
|
+
"file_key": "FILE_KEY",
|
|
646
|
+
"node_id": "1:2"
|
|
647
|
+
}
|
|
648
|
+
]
|
|
649
|
+
}' \
|
|
650
|
+
"https://api.figma.com/v1/dev_resources"
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**Response:**
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
interface CreateDevResourcesResponse {
|
|
657
|
+
links_created: DevResource[];
|
|
658
|
+
errors: Array<{
|
|
659
|
+
file_key: string;
|
|
660
|
+
node_id: string;
|
|
661
|
+
error: string;
|
|
662
|
+
}>;
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
**Constraints:**
|
|
667
|
+
|
|
668
|
+
| Constraint | Limit |
|
|
669
|
+
|-----------|-------|
|
|
670
|
+
| Max dev resources per node | 10 |
|
|
671
|
+
| Duplicate URLs per node | Not allowed |
|
|
672
|
+
| File key | Must be valid and accessible |
|
|
673
|
+
|
|
674
|
+
#### PUT /v1/dev_resources
|
|
675
|
+
|
|
676
|
+
Update dev resources in bulk.
|
|
677
|
+
|
|
678
|
+
```bash
|
|
679
|
+
curl -X PUT \
|
|
680
|
+
-H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
|
|
681
|
+
-H "Content-Type: application/json" \
|
|
682
|
+
-d '{
|
|
683
|
+
"dev_resources": [
|
|
684
|
+
{
|
|
685
|
+
"id": "dev-resource-id-123",
|
|
686
|
+
"name": "Updated Component Name",
|
|
687
|
+
"url": "https://github.com/org/repo/blob/main/src/NewButton.tsx"
|
|
688
|
+
}
|
|
689
|
+
]
|
|
690
|
+
}' \
|
|
691
|
+
"https://api.figma.com/v1/dev_resources"
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**Response:**
|
|
695
|
+
|
|
696
|
+
```ts
|
|
697
|
+
interface UpdateDevResourcesResponse {
|
|
698
|
+
links_updated: string[]; // IDs of successfully updated resources
|
|
699
|
+
errors: Array<{
|
|
700
|
+
id: string;
|
|
701
|
+
error: string;
|
|
702
|
+
}>;
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
#### DELETE /v1/files/:file_key/dev_resources/:dev_resource_id
|
|
707
|
+
|
|
708
|
+
Delete a single dev resource.
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
curl -X DELETE \
|
|
712
|
+
-H "X-Figma-Token: YOUR_FIGMA_TOKEN" \
|
|
713
|
+
"https://api.figma.com/v1/files/FILE_KEY/dev_resources/DEV_RESOURCE_ID"
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
Returns no content on success.
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
### Dev Resources Use Cases
|
|
721
|
+
|
|
722
|
+
#### Linking Components to Source Code
|
|
723
|
+
|
|
724
|
+
After generating code from Figma components, attach links back to the generated files:
|
|
725
|
+
|
|
726
|
+
```ts
|
|
727
|
+
async function linkComponentsToCode(
|
|
728
|
+
fileKey: string,
|
|
729
|
+
mappings: Array<{ nodeId: string; repoUrl: string; componentName: string }>
|
|
730
|
+
) {
|
|
731
|
+
const response = await fetch('https://api.figma.com/v1/dev_resources', {
|
|
732
|
+
method: 'POST',
|
|
733
|
+
headers: {
|
|
734
|
+
'X-Figma-Token': process.env.FIGMA_TOKEN!,
|
|
735
|
+
'Content-Type': 'application/json',
|
|
736
|
+
},
|
|
737
|
+
body: JSON.stringify({
|
|
738
|
+
dev_resources: mappings.map(m => ({
|
|
739
|
+
name: `${m.componentName} (Source)`,
|
|
740
|
+
url: m.repoUrl,
|
|
741
|
+
file_key: fileKey,
|
|
742
|
+
node_id: m.nodeId,
|
|
743
|
+
})),
|
|
744
|
+
}),
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
const result = await response.json();
|
|
748
|
+
|
|
749
|
+
if (result.errors?.length > 0) {
|
|
750
|
+
console.warn('Some dev resources failed:', result.errors);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return result.links_created;
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
#### Surfacing Generated Code in Dev Mode
|
|
758
|
+
|
|
759
|
+
Create dev resources that link to generated code output (e.g., a code preview URL):
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
async function attachCodePreview(
|
|
763
|
+
fileKey: string,
|
|
764
|
+
nodeId: string,
|
|
765
|
+
previewUrl: string
|
|
766
|
+
) {
|
|
767
|
+
await fetch('https://api.figma.com/v1/dev_resources', {
|
|
768
|
+
method: 'POST',
|
|
769
|
+
headers: {
|
|
770
|
+
'X-Figma-Token': process.env.FIGMA_TOKEN!,
|
|
771
|
+
'Content-Type': 'application/json',
|
|
772
|
+
},
|
|
773
|
+
body: JSON.stringify({
|
|
774
|
+
dev_resources: [{
|
|
775
|
+
name: 'Generated Code Preview',
|
|
776
|
+
url: previewUrl,
|
|
777
|
+
file_key: fileKey,
|
|
778
|
+
node_id: nodeId,
|
|
779
|
+
}],
|
|
780
|
+
}),
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
#### Design-to-Code Traceability
|
|
786
|
+
|
|
787
|
+
Build a bidirectional link between Figma designs and implemented code:
|
|
788
|
+
|
|
789
|
+
```ts
|
|
790
|
+
// 1. Fetch all dev resources for a file
|
|
791
|
+
async function getDevResources(fileKey: string): Promise<DevResource[]> {
|
|
792
|
+
const res = await fetch(
|
|
793
|
+
`https://api.figma.com/v1/files/${fileKey}/dev_resources`,
|
|
794
|
+
{ headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
|
|
795
|
+
);
|
|
796
|
+
const data = await res.json();
|
|
797
|
+
return data.dev_resources;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// 2. Build a node → code URL mapping
|
|
801
|
+
async function buildTraceabilityMap(fileKey: string): Promise<Map<string, string[]>> {
|
|
802
|
+
const resources = await getDevResources(fileKey);
|
|
803
|
+
const map = new Map<string, string[]>();
|
|
804
|
+
|
|
805
|
+
for (const resource of resources) {
|
|
806
|
+
const urls = map.get(resource.node_id) || [];
|
|
807
|
+
urls.push(resource.url);
|
|
808
|
+
map.set(resource.node_id, urls);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return map;
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
---
|
|
816
|
+
|
|
817
|
+
### Dev Mode Annotations
|
|
818
|
+
|
|
819
|
+
Annotations in Dev Mode provide additional context for developers inspecting designs.
|
|
820
|
+
|
|
821
|
+
#### Ready for Dev Status
|
|
822
|
+
|
|
823
|
+
Designers mark frames or sections as "Ready for dev" to signal that a design is complete and ready for implementation. This status is visible in Dev Mode and can be used to filter which components to process.
|
|
824
|
+
|
|
825
|
+
#### Plugin API Access
|
|
826
|
+
|
|
827
|
+
```ts
|
|
828
|
+
// Access annotations on a node
|
|
829
|
+
if ('annotations' in node) {
|
|
830
|
+
const annotations = node.annotations;
|
|
831
|
+
// Annotations contain measurements, notes, and status information
|
|
832
|
+
}
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
#### How Annotations Relate to Dev Resources
|
|
836
|
+
|
|
837
|
+
- **Annotations** are designer-created notes about a design's intent, measurements, and specifications. They live within Figma.
|
|
838
|
+
- **Dev Resources** are links to external developer references (code, docs, stories). They are created via the REST API or manually in Dev Mode.
|
|
839
|
+
|
|
840
|
+
Both appear in the Inspect panel and together provide a complete handoff context: annotations explain the "what" and "why" of the design, while dev resources point to the "where" and "how" of the implementation.
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
### Codegen vs Standard Plugin: Quick Comparison
|
|
845
|
+
|
|
846
|
+
| Aspect | Standard Plugin | Codegen Plugin |
|
|
847
|
+
|--------|:--------------:|:--------------:|
|
|
848
|
+
| `editorType` | `["figma"]` or `["figma", "figjam"]` | `["dev"]` |
|
|
849
|
+
| `capabilities` | None required | `["codegen"]` (required) |
|
|
850
|
+
| UI | iframe via `figma.showUI()` | No visible UI (code in Inspect panel) |
|
|
851
|
+
| Trigger | User runs from Plugins menu | User selects node in Dev Mode |
|
|
852
|
+
| Output | Side effects (node creation, export, etc.) | `CodegenResult[]` displayed in Inspect panel |
|
|
853
|
+
| Document access | Full read/write | Read-only |
|
|
854
|
+
| Primary use case | Design automation, export, analysis | Code generation for developers |
|
|
855
|
+
| `figma.mode` | `"default"` | `"codegen"` |
|
|
856
|
+
| Network access | Via UI iframe proxy | Via UI iframe proxy (if using hidden UI) |
|
|
857
|
+
| Timeout | None (runs until `closePlugin()`) | 3-second timeout on generate callback |
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
### Error Handling
|
|
862
|
+
|
|
863
|
+
#### Codegen Plugin Errors
|
|
864
|
+
|
|
865
|
+
| Error | Cause | Solution |
|
|
866
|
+
|-------|-------|----------|
|
|
867
|
+
| Generate callback timeout | Callback exceeds 3 seconds | Optimize code generation; pre-cache data; use simpler algorithms |
|
|
868
|
+
| Node properties unavailable | Accessing properties not on the node type | Check `node.type` before accessing type-specific properties |
|
|
869
|
+
| Font not loaded | Accessing text properties without loading font | Call `figma.loadFontAsync()` before reading text properties |
|
|
870
|
+
|
|
871
|
+
#### Dev Resources REST API Errors
|
|
872
|
+
|
|
873
|
+
| Status | Meaning | Action |
|
|
874
|
+
|--------|---------|--------|
|
|
875
|
+
| `400` | Invalid parameters (bad node_id, invalid URL) | Verify request body structure and field values |
|
|
876
|
+
| `401` | Authentication failed | Verify token and `file_dev_resources:read/write` scope |
|
|
877
|
+
| `403` | Insufficient permissions | Confirm file access permissions |
|
|
878
|
+
| `404` | Dev resource not found (DELETE only) | Verify the dev_resource_id exists |
|
|
879
|
+
| `429` | Rate limited | Use `Retry-After` header. See `figma-api-rest.md` for backoff strategy |
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
## Cross-References
|
|
884
|
+
|
|
885
|
+
- **`figma-api-rest.md`** — Core REST API reference (authentication, rate limits, file endpoints)
|
|
886
|
+
- **`figma-api-plugin.md`** — Standard plugin development (sandbox model, SceneNode types, IPC patterns)
|
|
887
|
+
- **`figma-api-variables.md`** — Variables API for design tokens (consumed during code generation)
|
|
888
|
+
- **`figma-api-webhooks.md`** — Webhooks v2 DEV_MODE_STATUS_UPDATE event for reacting to "Ready for dev" changes
|
|
889
|
+
- **`design-to-code-layout.md`** — Auto Layout to Flexbox mapping rules (used in codegen callbacks)
|
|
890
|
+
- **`design-to-code-visual.md`** — Visual property extraction (fills, strokes, effects for CSS generation)
|
|
891
|
+
- **`design-to-code-typography.md`** — Typography mapping (font families, sizes, line height for CSS)
|
|
892
|
+
- **`design-to-code-semantic.md`** — Semantic HTML generation patterns
|
|
893
|
+
- **`css-strategy.md`** — Layered CSS approach (Tailwind + CSS Custom Properties + CSS Modules)
|
|
894
|
+
- **`plugin-codegen.md`** — Production codegen plugin development patterns (generation pipeline, preferences, code quality, responsive output)
|