workflow-skills/generate-project-plan/SKILL.md
Generate a FigJam project plan board from a PRD plus codebase context. Interactive flow: research → propose sections → per-section deep research → per-section content + block-shape proposal → create FigJam → skeleton → fill → diagrams → wrap. Each content block (section, nested section, intro callout, table, multi-column text, sticky column, diagram section, metadata strip) has its own subskill reference file. Use when the user asks for 'project plan in FigJam', 'interactive project plan', '/generate-project-plan', or provides a PRD and wants per-section confirmation on content + rendering.
npx skillsauth add figma/mcp-server-guide generate-project-planInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Turn a PRD (plus optional codebase grounding) into a FigJam project plan board. Section set is not fixed — the skill proposes candidates from the research and the user picks which to include. For each picked section, the skill proposes content + rendering shape (block) and the user confirms.
Foundation skills (load by name; available in figma/mcp-server-guide):
figma-use — Load once per session. Stays in context for all use_figma calls.figma-use-figjam — Re-load before every use_figma call.figma-generate-diagram — Re-load before every generate_diagram call.Foundation references (in this plugin):
foundation/palette.md — section + sticky + text palette constants (hex/255).foundation/layout.md — canvas geometry, sizing rules, placeholder lifecycle.foundation/plugin-api-traps.md — documented traps for FigJam use_figma.foundation/codebase-grounding.md — Step 1 expansion rules.Section catalog:
section-catalog.md — the ~10 candidate sections with default blocks and palette.Block subskills (one per content type — re-load the one(s) you need before each use_figma fill call):
| Block | File | When to load |
|---|---|---|
| Top-level section | blocks/section.md | Skeleton pass (Step 6) and every fill call (Step 7) |
| Nested section | blocks/nested-section.md | Fills that group sub-content (e.g. "Design Decisions 1/2/3") |
| Intro callout | blocks/intro-callout.md | Fills that open with a highlighted intro (e.g. Motivation) |
| Text primitives | blocks/text-primitives.md | Any fill that uses body paragraphs, H3 subheaders, or bulleted lists |
| Table | blocks/table.md | Fills with structured data (Resources, Goals, Dependencies, Rollout, Milestones) |
| Multi-column text | blocks/multi-column-text.md | Fills with 2–4 option columns (Design Decisions alternatives) |
| Sticky column | blocks/sticky-column.md | Fills with lists of stickies (Success Metrics, Risks, Open Questions) |
| Diagram section | blocks/diagram-section.md | Right-column diagram sections (Step 8) |
| Metadata strip | blocks/metadata-strip.md | Skeleton pass — one metadata strip at top of board |
Also pass skillNames: "figma-use,figma-use-figjam,generate-project-plan" on use_figma calls (logging only).
These are derived from a canonical reference board. Read the source-of-truth files for the full constants; this section is a single-place summary so an agent can answer "what color / size / font / padding?" without hunting.
Every left-column section uses two coordinated colors of the same hue: a very-pale ARCH_PALE background, and a slightly-more-saturated FigJam-SECTION palette color for any table header inside that section. Right-column diagram sections are pure white.
| Section bg (ARCH_PALE.X) | Table header (TABLE_HEADER.lightX) | Hue |
|---|---|---|
| #F8F5FF | #DCCCFF | violet |
| #EBFFEE | #CDF4D3 | green |
| #DBF0FF | #C2E5FF | blue |
| #F5FBFF (alt) | #C2E5FF | pale blue |
| #FFF7F0 | #FFE0C2 | orange |
| #F1FEFD | #C6FAF6 | teal |
| #FFFBF0 | #FFEC BD | yellow |
| #FFEEF8 | #FFC2EC | pink |
| #FFEEE8 | #FFCDC2 | red |
Source: references/foundation/palette.md. Never use the dark-saturated palette (#874FFF, #3DADFF, etc.) for table headers — that's for FigJam's standalone tables, not project-plan boards.
Architecture-diagram subgraph colors are auto-applied by generate_diagram and must not be overridden. Their canonical values: client #AFF4C6 rounded-rect, gateway #FFFFFF square (diamond if labeled "Load Balancer"/"ALB"/"LB"), service #E4CCFF square, datastore #BDE3FF cylinder, external #FFFFFF PREDEFINED_PROCESS, async #BDE3FF ENG_QUEUE.
| Element | Size | Font | Color |
|---|---|---|---|
| H1 (board title) | 40 | Inter Medium | #1E1E1E |
| H2 (section title — first child of every section) | 40 | Inter Medium | #1E1E1E |
| H3 — full-width subhead (e.g. "Resources" inside Motivation) | 40 | Inter Medium | #1E1E1E |
| H3 — nested-section header (e.g. "Design Decision 1: …") | 32 | Inter Medium | #1E1E1E |
| H3 — column title in 2/3/4-col layouts (Risks col, Goals col) | 24 | Inter Medium | #1E1E1E |
| Body text | 16 | Inter Medium | #1E1E1E |
| Table cells (header AND body) | 16 | Inter Bold | #1E1E1E |
The three different H3 sizes are deliberate. 40 = matches H2 weight when subhead is alone in the section. 32 = sub-section header inside a child section (672px inner width). 24 = column title in narrow contexts (≤ 224px col width). Pick by container width, not by semantic depth.
Always load both Inter Medium AND Inter Bold at the top of any use_figma script that creates tables (Bold) plus any other text (Medium).
| Property | Value |
|---|---|
| section.fills | [{ type: 'SOLID', color: ARCH_PALE.X }] (left column) or ARCH_PALE.white (right column / diagrams) |
| section.name | "" — empty string. NO FigJam title-bar label. The H2 inside is the only title. |
| Inner padding (all 4 sides) | 32 (current default; reference uses 40-50, kept at 32 for now) |
| First child position | (32, 32) |
| Width (left column) | 800 |
| Width (right column / diagram) | max(1200, diag.width + 64) after diagram is reparented |
| Vertical gap between sections (inside the wrapper) | 64 |
| Hug behavior | Manual: call section.resizeWithoutConstraints(w, maxChildBottom + 32) after appending children. Sections do NOT auto-grow. |
| Placeholder during build | placeholder = true in skeleton pass; placeholder = false at the end of the section's fill. |
The board has one outer wrapper (unlabeled, white) plus the diagram column:
(_, 0). Contains, in order from top:
(64, 64) (= section padding)(64, h1.y + h1.height + 16) — 16px gap below H1; 32px gap between each body cell(columnWrapperRight + 64, 0). Each diagram is its own un-wrapped white SECTION; the top diagram's y aligns with the column wrapper's y (= 0). Diagrams stack with 64px gutter.The metadata is embedded in the column wrapper (NOT a separate section). One column wrapper holds everything text-related.
Constants:
wrapper.y (same horizontal axis as the wrapper top edge)const PAD = 64;
const leftIds = [/* all left-column section ids in order */];
const sections = [];
for (const id of leftIds) sections.push(await figma.getNodeByIdAsync(id));
// Compute bbox of the column in page coords
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const s of sections) {
minX = Math.min(minX, s.x); minY = Math.min(minY, s.y);
maxX = Math.max(maxX, s.x + s.width); maxY = Math.max(maxY, s.y + s.height);
}
const wrapper = figma.createSection();
wrapper.name = ""; // STRICT
wrapper.fills = [{ type: 'SOLID', color: WHITE }]; // ARCH_PALE.white
wrapper.resizeWithoutConstraints((maxX - minX) + 2*PAD, (maxY - minY) + 2*PAD);
wrapper.x = minX - PAD;
wrapper.y = minY - PAD;
// Reparent + translate to keep visual positions
for (const s of sections) {
const newX = (s.x - minX) + PAD;
const newY = (s.y - minY) + PAD;
wrapper.appendChild(s);
s.x = newX;
s.y = newY;
}
The wrapper has no header (no H2 inside) and no name — it's a pure container. Don't repeat the project title here; that lives in the metadata strip.
Position the diagram column after the wrapper exists:
const wrapperRight = wrapper.x + wrapper.width;
const diagramX = wrapperRight + 64; // 64px horizontal gap matches inter-section gutter
let y = wrapper.y; // align top with wrapper's top edge
for (const id of diagramSectionIdsInOrder) {
const d = await figma.getNodeByIdAsync(id);
d.x = diagramX;
d.y = y;
y += d.height + 64; // 64px vertical gutter between stacked diagrams
}
| Between | Gap | |---|---| | Section top edge → H2 (first child) | 32 (= padding) | | H2 → next child (body / intro callout / H3 / table / first column) | 24 | | Body paragraph → H3 | 24 | | H3 (any size: 40 / 32 / 24) → next child (body / table / list / column content) | 24 | | Body → body | 24 | | List → next block | 24 | | Last child bottom → section bottom edge | 32 (= padding) |
Always position children using prevChild.y + prevChild.height + 24 — never use a fixed offset like prevChild.y + 60. The H3 has three different sizes (40 / 32 / 24), so a fixed offset is wrong by definition; always read prevChild.height after the font size is set.
When you change a header's font size after the fact, you MUST re-stack downstream children. Setting h3.fontSize = 40 grows the node's height; if the next child's y was computed before the resize, it will overlap.
Inter Bold 16px (NOT Medium).#1E1E1E charcoal on a light fill (matching the section's hue).generate_diagram with useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026" produces multiple page-level nodes (1–2 subgraph SECTIONs + bare SHAPE_WITH_TEXTs + CONNECTORs), NOT a single container. To wrap them in a section:
bboxW + 64 × bboxH + 64 + 64 (HEADER_BLOCK = 40 H2 + 24 gap), fill ARCH_PALE.white.c.connectorStart = c.connectorStart) is NOT reliable: short connectors re-route fine, but long-bend connectors retain stale elbow waypoints and extend hundreds of pixels outside the section. Delete + figma.createConnector() from captured spec produces a clean route every time. See blocks/diagram-section.md for the spec-capture + recreate pattern.fontName + fontSize + fills ALL required. A fresh connector's text sublayer has no usable defaults. Set ALL FOUR: c.text.fontName = { family: 'Inter', style: 'Medium' }, c.text.fontSize = 14, c.text.characters = label, c.text.fills = [{ type: 'SOLID', color: CHARCOAL }]. Default text.fills is [] (empty array) so the label renders transparent and is invisible — read-back of c.text.characters lies; verify with a screenshot.Diagram-section convention: section.name = "", H2 text node inside as the title (matches the rest of the board).
| Don't use | Use instead | Why |
|---|---|---|
| #CDF4D3 (FigJam lightGreen) for section bg | #EBFFEE (ARCH_PALE.green) | Too saturated next to diagrams |
| #C2E5FF (FigJam lightBlue) for section bg | #DBF0FF or #F5FBFF (ARCH_PALE.blue / blueLite) | Same |
| #874FFF (FigJam dark violet) for table header | #DCCCFF (FigJam lightViolet) | Reference uses light-on-pale, not dark-on-pale |
| White text on dark table header | #1E1E1E charcoal text on light header | Same — pale-on-pale convention |
| Inter Medium for table cells | Inter Bold | Reference uses Bold for all cells |
| section.name = "Goals" (or any non-empty string) | section.name = "" | Reference uses empty names; H2 inside is the title |
| Reparenting only one subgraph from a generated diagram | Reparent ALL page-level nodes (SECTIONs + SHAPEs + CONNECTORs) | Architecture diagrams are not single nodes |
| Trusting c.connectorStart = c.connectorStart to re-route every connector | After reparent, delete and recreate every connector from a captured spec | Long-bend connectors retain stale elbow waypoints; only figma.createConnector() produces a fresh route |
| Setting only c.text.characters = label on a fresh connector | Set c.text.fontName, c.text.fontSize, c.text.characters, AND c.text.fills | Defaults: fontSize=missing, fills=[] (empty array → transparent) → label invisible despite correct read-back |
Every step below uses this shape. Read the step, then execute.
## Step N — <Name> [Type: Research | Confirm | Write]
Inputs required: …
Ask if missing: …
Tools / refs loaded: …
Do: (3–6 action bullets)
Checkpoint: (Research → self-check; Confirm → AskUserQuestion; Write → screenshot + AskUserQuestion)
Three step types:
AskUserQuestion.AskUserQuestion.Inputs required
Ask if missing
Tools / refs loaded
Read, Glob, Grep.foundation/codebase-grounding.md.Do
codebase-grounding.md — bounded 20-file cap, depth-1 imports, walk up to CLAUDE.md/ARCHITECTURE.md/OWNERS.files_read, services, external_deps, key_modules, architecture_notes, ownership, expansion_truncated.Self-check
Inputs required
Tools / refs loaded
section-catalog.md.AskUserQuestion.Do
AskUserQuestion with a multiSelect question per batch of ≤4 candidates (max 4 questions per call, 4 options each → up to 16 candidates per call). Each option's label is the section title; description is the 1-line summary.Checkpoint (AskUserQuestion)
approved_sections.Inputs required
approved_sections from Step 2; tech-context from Step 1.Tools / refs loaded
Read, Grep, Glob (optional).Do
approved_sections, look up its catalog entry. The catalog declares what the section needs (e.g. Dependencies needs cross-team services + external deps + blockers).Self-check
approved_sections entry has either ready (no gaps) or a specific non-empty gap list.Inputs required
Tools / refs loaded
AskUserQuestion.section-catalog.md.blocks/table.md for Dependencies).Do, per section (one at a time, or small batch if trivial):
AskUserQuestion for bounded choices.section-catalog.md; offer alternative shapes where sensible (e.g. "As a table, or as a multi-column layout?").AskUserQuestion: "Use this content + shape? [Yes / Edit / Skip this section]."skipped; do not write it to the board.Checkpoint
approved, edited+approved, or skipped. No board writes yet.Inputs required
approved_sections (non-skipped); project title; planKey (Figma team plan).Tools / refs loaded
create_new_file MCP tool.whoami MCP tool (for planKey if not known).use_figma (once).figma-use (already in context from Step 5 onward).figma-use-figjam (re-loaded for the probe).AskUserQuestion.Do
planKey: call whoami. If one plan → use it. If multiple → AskUserQuestion which team.create_new_file with { planKey, fileName: "<project title>", editorType: "figjam" }. Capture file_key + file_url.use_figma):const page = figma.currentPage;
return {
rootName: figma.root.name,
editorType: figma.editorType,
pageCount: figma.root.children.length,
firstPageName: figma.root.children[0].name,
currentPageChildrenCount: page.children.length,
};
Expect editorType === "figjam" and empty page. If not, halt and report.
Checkpoint (probe output + AskUserQuestion)
file_url.<file_url> — proceed to skeleton? [Yes / Cancel]." Cancel = stop, leave empty file.Inputs required
approved_sections in taxonomy order; palette from foundation/palette.md; layout constants from foundation/layout.md.Tools / refs loaded
use_figma (one call).figma-use-figjam/SKILL.md.blocks/section.md and blocks/metadata-strip.md.foundation/palette.md, foundation/layout.md.Do
blocks/metadata-strip.md (H1 + 4 body texts at board (0, 0)–(0, ~100)).blocks/section.md — colored bg from ARCH_PALE, section.name = "" (no title-bar label — STRICT), placeholder = true, resizeWithoutConstraints(LEFT_COL_W, DEFAULT_H) for left-column sections or resizeWithoutConstraints(RIGHT_COL_W_MIN, DEFAULT_H) for right-column diagram sections.(0, SECTION_TOP_Y + cumulative_y). Right-column sections at (832, SECTION_TOP_Y + cumulative_y_right).{ createdNodeIds: { metadataStrip: {...}, sections: { <slug>: "<id>", ... } }, status: "skeleton-complete" }. (The slug is internal, used to look up palette + drive Step 7 fills. It is never written to section.name.)await figma.currentPage.screenshot({ scale: 0.3 }).Checkpoint (screenshot + AskUserQuestion)
Inputs required
Tools / refs loaded
use_figma (one call per section).figma-use-figjam/SKILL.md.blocks/section.md always.blocks/text-primitives.md if body/H3/list.blocks/table.md if table.blocks/multi-column-text.md if multi-column.blocks/nested-section.md if nested subsections.blocks/intro-callout.md if highlighted intro.blocks/sticky-column.md if stickies.Do, per section:
await figma.getNodeByIdAsync(sectionId) — confirm type is SECTION.blocks/sticky-column.md.section.resizeWithoutConstraints(LEFT_COL_W, computed_height).section.placeholder = false.await section.screenshot() inline.return { mutatedNodeIds: [...], sectionHeight, screenshotIncluded: true }.Checkpoint (screenshot + AskUserQuestion)
<name> done? [Yes / Edit this section / Skip rest]." Edit = targeted fix; Skip rest = exit fill loop (user will finalize manually).After all left-column fills: run the re-stack pass (single use_figma) to fix cumulative Y based on actual resized heights:
let y = SECTION_TOP_Y;
for (const id of leftColumnSectionIdsInOrder) {
const sec = await figma.getNodeByIdAsync(id);
sec.y = y;
sec.x = 0;
y += sec.height + 32;
}
return { mutatedNodeIds: leftColumnSectionIdsInOrder };
Then run the outer-wrapper pass (single use_figma) — wrap all left-column sections in an unlabeled white outer SECTION (see "Outer column wrapper" in the visual conventions block above). This is STRICT; do not skip.
Inputs required
Tools / refs loaded
generate-diagram/SKILL.md before each generate_diagram call.figma-use-figjam/SKILL.md + blocks/diagram-section.md before each reparent use_figma call.generate_diagram MCP tool, use_figma.Do, per diagram:
use_figma: create the right-column section (white fill, H2 header, placeholder = true) per blocks/diagram-section.md.generate_diagram: compose Mermaid from tech-context; architecture diagrams use useArchitectureLayoutCode: "FIGMA_DIAGRAM_2026".use_figma: locate the generated diagram node, reparent into the section (section.appendChild(diag)), position below H2, resize section to fit. placeholder = false.await section.screenshot().Failure handling: if generate_diagram fails, leave a text placeholder in the section reading "Diagram generation failed: <message>. Regenerate manually." Continue to the next diagram.
Checkpoint (screenshot + AskUserQuestion)
<name> looks right? [Yes / Regenerate / Skip]."Inputs required
file_url from Step 5.Tools / refs loaded
use_figma (one call for full-page screenshot).Do
await figma.currentPage.screenshot({ scale: 0.25 }) — full board.placeholder = true, metadata strip visible.use_figma script. Do not regenerate from scratch.✅ Project plan written to FigJam.
File: <file_url>
Sections: <N text + N diagram>
Files referenced during grounding: <count>
Checkpoint (screenshot + AskUserQuestion)
use_figma call.createdNodeIds / mutatedNodeIds from every write script.hex/255 notation for all palette colors (see foundation/palette.md).ARCH_PALE palette, NOT the FigJam standard SECTION palette. ARCH_PALE colors (#EBFFEE, #F8F5FF, #F5FBFF, #FFF7F0, etc.) visually pair with the architecture-diagram subgraph wrappers that generate_diagram produces. The FigJam SECTION palette (#CDF4D3, #C2E5FF, #DCCCFF, #FFE0C2) is too saturated and causes visible color clash next to diagrams. See foundation/palette.md.section.name = "" on every project-plan section (left-column, right-column, and nested children). The user-facing title is rendered as the H2 text node inside the section, NOT via FigJam's section title-bar label. The reference board uses empty section names; setting a non-empty name produces a duplicate label that visually clutters the board.use_figma script errors: atomic — no changes made. Read the error, fix, retry."/generate-project-plan", "interactive project plan", "project plan", "make a FigJam project plan", "PRD to FigJam".
development
Use this skill alongside figma-use when the task involves translating an application page, view, or multi-section layout into Figma. Triggers: 'write to Figma', 'create in Figma from code', 'push page to Figma', 'take this app/page and build it in Figma', 'create a screen', 'build a landing page in Figma', 'update the Figma screen to match code', 'convert this modal/dialog/drawer/panel to Figma'. This is the preferred workflow skill whenever the user wants to build or update a full page, modal, dialog, drawer, sidebar, panel, or any composed multi-section view in Figma from code or a description. Discovers design system components, variables, and styles from Code Connect files, existing screens, and library search, then imports them and assembles views incrementally section-by-section using design system tokens instead of hardcoded values.
tools
This skill helps agents use Figma's use_figma MCP tool in the Slides context. Can be used alongside figma-use which has foundational context for using the use_figma tool.
tools
This skill helps agents use Figma's use_figma MCP tool in the FigJam context. Can be used alongside figma-use which has foundational context for using the use_figma tool.
tools
MANDATORY prerequisite — load this skill BEFORE every `generate_diagram` tool call. NEVER call `generate_diagram` directly without loading this skill first. Trigger whenever the user asks to create, generate, draw, render, sketch, or build a diagram — flowchart, architecture diagram, sequence diagram, ERD or entity-relationship diagram, state diagram or state machine, gantt chart, or timeline. Also trigger when the user mentions Mermaid syntax or wants a system architecture, decision tree, dependency graph, API call flow, auth handshake, schema, or pipeline visualized in FigJam. Routes to type-specific guidance, sets universal Mermaid constraints, and tells you when to use a different diagram type or skip the tool entirely (mindmaps, pie charts, class diagrams, etc.).