Install
$ agentstack add skill-thedesignproject-agent-skills-figma-generate-design ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Build / Update Screens and Views from Design System
Use this skill to create or update screens, views, and multi-section UI containers in Figma by reusing the published design system — components, variables, and styles — rather than drawing primitives with hardcoded values. This includes full pages, modals, dialogs, drawers, sidebars, panels, and any composed view with multiple sections. The key insight: the Figma file likely has a published design system with components, color/spacing variables, and text/effect styles that correspond to the codebase's UI components and tokens. Find and use those instead of drawing boxes with hex colors.
MANDATORY: You MUST also load [figma-use](../figma-use/SKILL.md) before any use_figma call. That skill contains critical rules (color ranges, font loading, etc.) that apply to every script you write.
Always pass skillNames: "figma-generate-design" when calling use_figma as part of this skill. This is a logging parameter — it does not affect execution.
Skill Boundaries
- Use this skill when the deliverable is a composed Figma view (new or updated) — full-page screens, modals, dialogs, drawers, sidebars, panels, or any multi-section container — built from design system component instances.
- If the user wants to create new reusable components or variants, use [figma-use](../figma-use/SKILL.md) directly.
- If the user wants to write Code Connect mappings, switch to [figma-code-connect](../figma-code-connect/SKILL.md).
Prerequisites
- Figma MCP server must be connected
- The target Figma file must have a published design system with components (or access to a team library)
- User should provide either:
- A Figma file URL / file key to work in
- Or context about which file to target (the agent can discover pages)
- Source code or description of the screen/view to build/update
Parallel Workflow with generatefigmadesign (Web Apps Only)
When building a screen from a web app that can be rendered in a browser, the best results come from running both approaches in parallel:
- In parallel:
- Start building the screen using this skill's workflow (use_figma + design system components)
- Run
generate_figma_designto capture a pixel-perfect screenshot of the running web app
- Once both complete: Update the usefigma output to match the pixel-perfect layout from the
generate_figma_designcapture. The capture provides the exact spacing, sizing, and visual treatment to aim for, while your usefigma output has proper component instances linked to the design system. If the capture contains images, transfer them to your use_figma output by copyingimageHashvalues from the capture's image fills (see Step 5 for details). - Once confirmed looking good: Delete the
generate_figma_designoutput — it was only used as a visual reference.
This combines the best of both: generate_figma_design gives pixel-perfect layout accuracy, while use_figma gives proper design system component instances that stay linked and updatable.
This parallel workflow is MANDATORY when the source contains images. The use_figma Plugin API cannot fetch external image URLs — it can only set image fills by copying imageHash values from nodes already in the file. generate_figma_design rasterizes all visible images into Figma, providing the hashes you need. If you skip the capture when images are present, image frames will be left blank.
For non-web apps (iOS, Android, etc.) or when updating existing screens, use the standard workflow below.
Required Workflow
Follow these steps in order. Do not skip steps.
> Hard gates — forbidden shortcuts: > > - Forbidden: search_design_system for component keys until 2a-i is complete and 2a-ii is attempted or logged N/A (e.g. "empty file, no existing screens"). > - Forbidden: Any use_figma call that mutates the canvas (Step 3+) until all Step 2 rows in the checklist below are filled in.
Step 1: Understand the Deliverable
Before touching Figma, understand what you're building:
- If building from code, read the relevant source files to understand the structure, sections, and which components are used.
- Identify the major sections of the view (e.g., for a page: Header, Hero, Content Panels, Footer; for a modal: Title Bar, Form Sections, Action Bar; for a sidebar: Navigation, Content Area, Footer Actions).
- For each section, list the UI components involved (buttons, inputs, cards, navigation pills, accordions, etc.).
- Check whether the view contains any images (e.g., `
,, background images, product photos, avatars, icons loaded from URLs). If it does and this is a web app, you **must** run the parallelgeneratefigmadesign` capture workflow — start it immediately alongside Step 2 so the capture runs while you discover components. See "Parallel Workflow with generatefigmadesign" above.
Step 2: Collect Component Keys, Variables, and Styles
You need three things from the design system: components (buttons, cards, etc.), variables (colors, spacing, radii), and styles (text styles, effect styles like shadows). Don't hardcode hex colors or pixel values when design system tokens exist.
2a: Discover components
2a-i — REQUIRED: Check Code Connect for needed components. Starting from the component list you built in Step 1, check whether each component has a Code Connect file in the codebase. Code Connect files live next to the component source and are named by platform:
- TypeScript/JS:
*.figma.ts,*.figma.js - React (parser-based):
*.figma.tsx - Kotlin/Compose:
.ktfiles containing@FigmaConnect - Swift:
.swiftfiles containingFigmaConnect
For each component you need (e.g., Button, Card, Input), search for its Code Connect file — glob or grep by component name (e.g., **/Button.figma.tsx, **/Card.figma.ts). Only read files that match components you actually need.
From each matching Code Connect file, extract the Figma component URL. Parse fileKey and nodeId from the URL (convert hyphens to colons: 123-456 → 123:456). Then resolve component keys via use_figma:
Example: Code Connect file contains // url=https://figma.com/design/ABC123/File?node-id=609-35535. Parse fileKey = ABC123, nodeId = 609:35535. Run use_figma against the library file (fileKey ABC123, not the target file) to resolve the key:
const node = await figma.getNodeByIdAsync("609:35535");
const set = node?.parent?.type === "COMPONENT_SET" ? node.parent : node;
return { componentKey: set.key };
Batch multiple lookups in a single call. Use the returned keys with importComponentSetByKeyAsync() in Step 4.
Mark resolved components. If all components are resolved, skip 2a-ii and 2a-iii. If none of the needed components have Code Connect files, proceed to 2a-ii.
2a-ii — REQUIRED if unresolved components remain: Inspect existing screens. Check if the target file already contains screens using the same design system. A single use_figma call that walks an existing frame's instances gives you an exact, authoritative component map:
// Read-only discovery — skip invisible content inside instances (hidden
// variants etc.) for the hundreds-of-times-faster findAllWithCriteria.
figma.skipInvisibleInstanceChildren = true;
const frame = figma.currentPage.findOne(n => n.name === "Existing Screen");
const uniqueSets = new Map();
frame.findAllWithCriteria({ types: ["INSTANCE"] }).forEach(inst => {
const mc = inst.mainComponent;
const cs = mc?.parent?.type === "COMPONENT_SET" ? mc.parent : null;
const key = cs ? cs.key : mc?.key;
const name = cs ? cs.name : mc?.name;
if (key && !uniqueSets.has(key)) {
uniqueSets.set(key, { name, key, isSet: !!cs, sampleVariant: mc.name });
}
});
return [...uniqueSets.values()];
Match results against your unresolved components. Mark any newly resolved. If all components are resolved, skip 2a-iii.
2a-iii — LAST RESORT: search_design_system. Only if components remain unresolved after completing both 2a-i and 2a-ii.
Before searching, call get_libraries to discover which libraries are available for the file. This returns two lists: libraries already added to the file and libraries available to add (community UI kits and org libraries). Each entry includes a libraryKey you can pass to search_design_system via the includeLibraryKeys param to scope your search to specific libraries instead of searching across everything.
// Step 1: Discover available libraries
get_libraries({ fileKey })
// Returns: {
// libraries_added_to_file: [...],
// libraries_available_to_add: [...],
// libraries_available_to_add_next_offset: number | null
// }
// Step 2: Search within a specific library using its libraryKey
search_design_system({ query: "button", fileKey, includeLibraryKeys: ["lk-abc123..."] })
Org libraries in libraries_available_to_add are paginated (20 per page). When libraries_available_to_add_next_offset is non-null, more org libraries are available — call get_libraries again with offset set to that value to fetch the next page. Community UI kits only appear on the first page. If the user names a specific library you don't see in the current page, page further before giving up.
This is especially useful when the file has many libraries and you want targeted results (e.g. searching only within "iOS 26" or "Material 3" instead of getting matches from every library).
Search broadly — try multiple terms and synonyms (e.g., "button", "input", "nav", "card", "accordion", "header", "footer", "tag", "avatar", "toggle", "icon", etc.). Use includeComponents: true to focus on components.
Include component properties in your map — you need to know which TEXT properties each component exposes for text overrides. Create a temporary instance, read its componentProperties (and those of nested instances), then remove the temp instance.
Example component map with property info:
Component Map:
- Button → key: "abc123", type: COMPONENT_SET
Properties: { "Label#2:0": TEXT, "Has Icon#4:64": BOOLEAN }
- PricingCard → key: "ghi789", type: COMPONENT_SET
Properties: { "Device": VARIANT, "Variant": VARIANT }
Nested "Text Heading" has: { "Text#2104:5": TEXT }
Nested "Button" has: { "Label#2:0": TEXT }
2b: Discover variables (colors, spacing, radii)
Inspect existing screens first (same as components). Or use search_design_system with includeVariables: true.
> WARNING: Two different variable discovery methods — do not confuse them. > > - use_figma with figma.variables.getLocalVariableCollectionsAsync() — returns only local variables defined in the current file. If this returns empty, it does not mean no variables exist. Remote/published library variables are invisible to this API. > - search_design_system with includeVariables: true — searches across all linked libraries, including remote and published ones. This is the correct tool for discovering design system variables. > > Never conclude "no variables exist" based solely on getLocalVariableCollectionsAsync() returning empty. Always also run search_design_system with includeVariables: true to check for library variables before deciding to create your own.
Query strategy: search_design_system matches against variable names (e.g., "Gray/gray-9", "core/gray/100", "space/400"), not categories. Run multiple short, simple queries in parallel rather than one compound query:
- Primitive colors: "gray", "red", "blue", "green", "white", "brand"
- Semantic colors: "background", "foreground", "border", "surface", "text"
- Spacing/sizing: "space", "radius", "gap", "padding"
If initial searches return empty, try shorter fragments or different naming conventions — libraries vary widely ("grey" vs "gray", "spacing" vs "space", "color/bg" vs "background").
Inspect an existing screen's bound variables for the most authoritative results:
// Read-only discovery — skip invisible instance interiors for speed.
figma.skipInvisibleInstanceChildren = true;
const frame = figma.currentPage.findOne(n => n.name === "Existing Screen");
// boundVariables can live on any scene node — enumerating every scene type
// just to feed findAllWithCriteria is roughly the same as findAll(() => true)
// and is much noisier in script output.
const uniqueIds = new Set(
frame.findAll(() => true).flatMap(n =>
Object.values(n.boundVariables ?? {})
.flatMap(b => Array.isArray(b) ? b : [b])
.map(b => b?.id)
.filter(Boolean)
)
);
const variables = await Promise.all(
[...uniqueIds].map(id => figma.variables.getVariableByIdAsync(id))
);
return variables
.filter(Boolean)
.map(v => ({ name: v.name, id: v.id, key: v.key, type: v.resolvedType, remote: v.remote }));
For library variables (remote = true), import them by key with figma.variables.importVariableByKeyAsync(key). For local variables, use figma.variables.getVariableByIdAsync(id) directly.
See [variable-patterns.md](../figma-use/references/variable-patterns.md) for binding patterns.
2c: Discover styles (text styles, effect styles)
Search for styles using search_design_system with includeStyles: true and terms like "heading", "body", "shadow", "elevation". Or inspect what an existing screen uses:
// Read-only discovery — skip invisible instance interiors for speed.
figma.skipInvisibleInstanceChildren = true;
const frame = figma.currentPage.findOne(n => n.name === "Existing Screen");
const styles = { text: new Map(), effect: new Map() };
for (const node of frame.findAll(() => true)) {
// textStyleId is on TEXT and TEXT_PATH; effectStyleId is on most scene
// shape/container types. Use `in` guards to handle both without an
// exhaustive type list.
if ('textStyleId' in node && node.textStyleId) {
const s = figma.getStyleById(node.textStyleId);
if (s) styles.text.set(s.id, { name: s.name, id: s.id, key: s.key });
}
if ('effectStyleId' in node && node.effectStyleId) {
const s = figma.getStyleById(node.effectStyleId);
if (s) styles.effect.set(s.id, { name: s.name, id: s.id, key: s.key });
}
}
return {
textStyles: [...styles.text.values()],
effectStyles: [...styles.effect.values()]
};
Import library styles with figma.importStyleByKeyAsync(key), then apply with node.textStyleId = style.id or node.effectStyleId = style.id.
See [text-style-patterns.md](../figma-use/references/text-style-patterns.md) and [effect-style-patterns.md](../figma-use/references/effect-style-patterns.md) for details.
Step 3: Create the Wrapper Frame First
Do NOT build sections as top-level page children and reparent them later — moving nodes across use_figma calls with appendChild() silently fails and produces orphaned frames. Instead, create the wrapper first, then build each section directly inside it.
Create the wrapper in its own use_figma call. Position it away from existing content and return its ID:
// Find clear space
let maxX = 0;
for (const child of figma.currentPage.children) {
maxX = Math.max(maxX, child.x + child.width);
}
const wrapper = figma.createAutoLayout("VERTICAL");
// --- Size the wrapper based on container type ---
// Full page: wrapper.resize(1440, 100); wrapper.name = "Homepage";
// Modal/dialog: wrapper.resize(640, 100); wrapper.name = "Settings Modal";
// Drawer/sidebar: wrapper.resize(360, 100); wrapper.name = "Navigation Drawer";
// Panel: wrapper.resize(400, 100); wrapper.name = "Details Panel";
// Adapt width to match the source code's actual dimensions.
wrapper.name = "VIEW_NAME";
wrapper.primaryAxisAlignItems = "CENTER";
wrapper.counterAxisAlignItems = "CENTER";
wrapper.resize(WIDTH, 100);
wrapper.layoutSizingHorizontal = "FIXED";
wrapper.x = maxX + 200;
wrapper.y = 0;
return { success: true, wra
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [thedesignproject](https://github.com/thedesignproject)
- **Source:** [thedesignproject/agent-skills](https://github.com/thedesignproject/agent-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.