Generate a component documentation block in Figma. Builds a structured doc frame next to the selected component (or component set) containing description, variants with visual+intent descriptions, anatomy of composite parts, properties with default values, colors grouped by
Installs just this skill. Get the whole plugin for auto-invocation.
β‘ How it fires
How this skill gets triggered: by you, by Claude, or both.
Fires itselfClaude auto-loads it when your prompt matches the work.
You can call itInvoke it directly when you want it.
Slash command/figma-component-doc
ποΈ Context preview
The summary Claude sees to decide when to auto-load this skill.
Generate a component documentation block in Figma. Builds a structured doc frame next to the selected component (or component set) containing description, variants with visual+intent descriptions, anatomy of composite parts, properties with default values, colors grouped by
---name: figma-component-doc
description: "Generate a component documentation block in Figma. Builds a structured doc frame next to the selected component (or component set) containing description, variants with visual+intent descriptions, anatomy of composite parts, properties with default values, colors grouped by role, subcomponents with thumbnails, and a generation footer. Auto-discovers the file's own design tokens (text styles + color variables) β works on any Figma file regardless of design system. Use when user has a component selected in Figma and says 'document this component', 'document this', 'create docs for this', 'document the [name]', 'generate component documentation', 'add doc page for', 'component doc', 'figma component doc', or shares a Figma component URL asking for documentation. Manual-only. Requires the figma-console MCP / Desktop Bridge plugin (writes back into Figma)."
---# Component Documentation (Generic)
Create (or update) a component documentation block in Figma for the currently selected component. Auto-discovers the file's own design tokens (text styles + color variables) instead of assuming a specific design system. Works on any Figma file with any design system (or none).
**This skill writes nodes back into Figma.** It uses the local `figma-console` MCP, the same one `/figma-autopilot` and `/figma-fix` use.
---## Available figma-console tools
| Tool | Purpose |
|------|---------|
| `figma_get_status` | Check plugin connection |
| `figma_navigate` | Open a Figma file in Desktop |
| `figma_execute` | Run JS in Figma plugin context β used for discovery and complex node creation |
| `figma_create_child` | Create RECTANGLE / ELLIPSE / FRAME / TEXT / LINE under a parent |
| `figma_set_text` | Change text content / font size on an existing TEXT node |
| `figma_set_fills` | Change fill color on a node |
| `figma_set_strokes` | Change stroke on a node |
| `figma_resize_node` | Resize a node |
| `figma_move_node` | Reposition a node |
| `figma_rename_node` | Rename a layer |
| `figma_capture_screenshot` | Plugin-based screenshot β use after writes (immediate, fresh state) |
| `figma_take_screenshot` | REST-based screenshot β for initial visual context only |
**Use `figma_execute` for the bulk of node creation.** The dedicated tools handle individual property tweaks but cannot set auto-layout, bind variables to fills/text styles, or build nested structures in one shot. The skill needs both β `figma_execute` for the structural work, dedicated tools for any post-creation property fixes.
**Async API:** Always use `figma.getNodeByIdAsync()` and `figma.setCurrentPageAsync()` inside `figma_execute`. The sync versions throw.
---
## Step 0 β Prerequisites and connection check
Before doing anything, verify connections:
**a) Figma Desktop running with debug port:**
```bash
open -a 'Figma' --args --remote-debugging-port=9222
```
**b) Desktop Bridge plugin active in Figma.** Open Figma β Plugins β Desktop Bridge. Plugin must be running.
**c) Optional β VPN off.** figma-console MCP fails connection through some VPN configs.
**d) Run `figma_get_status`** to confirm the bridge is responding. If it errors with `No cloud relay session` or similar, this session is using the cloud-relay version of figma-console rather than the local one. Restart Claude Code to surface the local MCP, or use `figma_pair_plugin` to fall back to the cloud relay (works but less reliable).
**e) FIGMA_ACCESS_TOKEN in `~/.zshrc`** is only needed if you want to read component descriptions via REST API as a fallback. Skill doesn't require it.
If anything fails here, stop and surface the exact error. Don't keep going.
---
## Step 1 β Inspect the selection
Run a `figma_execute` script to read the current selection:
```javascript
const sel = figma.currentPage.selection[0];
if (!sel) return { error: 'No selection. Select a frame containing the component you want to document.' };
If `hasStyles` is `false`, the text helper falls back to raw font properties. If `hasVars` is `false`, it uses raw `#000000` / `#666666` / `#999999` hex values.
**Print the resolved `ST` and `VAR` map to the user before building.** Lets them sanity-check the picks and override before any nodes are created.
---
## Step 4 β Duplicate detection
Before creating anything, search for an existing doc frame:
If an existing doc frame is found, ask the user: *"Doc frame already exists for this component β rebuild it?"* If yes, clear its description and data children before repopulating. Never duplicate.
βββ [component last edited] β BODY, MUTED β only if available
```
Position the doc frame **to the right of the original component set** (e.g. `originalX + originalWidth + 200`, same Y). The original component set stays untouched in place.
**No clone of the component set inside the doc.** Earlier versions cloned the set into a parallel column, but it duplicated the VARIANTS card and left a tall empty stripe of whitespace beside it. The VARIANTS card already shows live instances of every variant β that's the source of truth.
**White wrap fill, 80px padding, 24px radius.** Makes the doc readable on any page background (some files have dark canvases that would hide black text), and gives the doc a self-contained surface.
---
## Card specs
All cards share:
- VERTICAL auto-layout
- 24px padding all sides
- Fill `#FFFFFF`, stroke 1px `#DEDEDE` INSIDE, cornerRadius 16
- Width FILL container, Height HUG
| Card | itemSpacing |
|---------------|-------------|
| VARIANTS | 20px |
| PROPERTIES | 12px |
| COLORS | 12px |
| SUBCOMPONENTS | 12px |
> Card chrome (white fill, gray stroke, 16px radius) is fixed neutral so docs read consistent across files. Card **content** uses the file's own tokens.
---
## VARIANTS card
**Title:** HEADING style, MUTED color, layer `"title"` β `"VARIANTS Β· N <propname>s"` where N is the variant count and `<propname>s` is the pluralized variant property name (e.g., `"VARIANTS Β· 5 types"`, `"VARIANTS Β· 3 sizes"`). Center-dot, never em-dash. Avoids redundancy with the per-variant labels below ("type = info", "size = sm" etc.).
**One `variant` frame per variant** (layer named `"variant"`):
- HORIZONTAL or VERTICAL auto-layout (see layout rule below)
- Contains: live component instance + `description` sub-frame
- `state` text β BODY_BOLD style, PRIMARY color, FILL β variant label e.g. `"type = single"`
- `description` text β BODY style, SECONDARY color, FILL β **two beats: visual change first, intent second**. Prefer `variant.description` from Figma if non-empty; auto-write only if empty.
- Visual beat: name the color/glyph difference inferred from the variant's primary fills ("Red glyph, soft-red surface."). The skill should inspect each variant's distinguishing fill (typically the leading icon) and translate hex to a color name.
- Intent beat: when to use ("Communicates a blocking failure.")
- No filler adjectives ("calm", "neutral", "approachable", "warm" etc.)
- Good: `"Red glyph, soft-red surface. Communicates a blocking failure."`
- Bad: `"Communicates a blocking failure."` (no visual cue)
- Bad: `"Brings warmth and approachability with a soft-red surface."` (filler adjectives)
- `usage` text β BODY_SB style, PRIMARY color, FILL β **only included if real usages were found in Step 2's `usages` array**. Format: `"Used in: parentA, parentB"`. If no real usages, omit the line entirely. Do NOT invent placeholder usages.
**Between variant rows:** insert a 1px hairline divider (a Rectangle with FILLΓ1 height, fill `#EAEAEA`). With 4+ variants, the lack of separators makes the card feel like one blob.
The HORIZONTAL primary case is what most components hit once the data column is wide enough (940px). It cuts doc length roughly in half compared to vertical stacking.
---
## ANATOMY card (composite components only)
Skip this card for **primitive** components (no nested instances) and for components whose decorated variant has fewer than 2 named child elements.
- For unnamed `"Frame N"` layers, infer from contents: TEXT child with largest font β "Title"; remaining TEXT β "Body"; INSTANCE β use main component name.
- Skip pure decorative frames (no text, no instance, no fill).
The anatomy card teaches readers the structure of a composite component faster than any text description.
**Group colors by their variable prefix folder.** Each unique top-level folder (e.g., `Text/*`, `Icons/*`, `CTA/*`, `Surface/*`) becomes a labeled section inside the card. Same-hex values across different roles (e.g., `Text/text-primary` and `Icons/icon-primary` both `#000000`) read as separate, intentional uses instead of looking like duplicates.
**Group structure** (VERTICAL, 8px gap):
- `group-label` text β HEADING style, MUTED color β uppercase folder name without trailing slash (e.g., `"TEXT"`, `"ICONS"`, `"CTA"`)
- One `color` row per fill in the group (HORIZONTAL, 12px gap, FILLΓHUG, counterAxisAlignItems CENTER):
- `Rectangle` 24Γ24, cornerRadius 6, 1px `#DEDEDE` stroke, fill = actual color
- `info` frame (VERTICAL, 2px gap, HUGΓHUG):
- `value` text β BODY_SB style, PRIMARY color β `"variableName Β· #HEX"` if variable-bound, else `"#HEX"`
- `description` text β BODY style, SECONDARY color β what this color is used for
**Group spacing:** 16px between groups inside the card.
**Unbound hex fills** (no variable): place in a `"SURFACE"` group if the color is light (lightness > 0.9), or `"OTHER"` otherwise.
- Live instance of the subcomponent at its used size (cap at 32Γ32 if larger to keep rows aligned)
- `info` frame (VERTICAL, 2px gap, FILLΓHUG):
- `name` text β BODY_BOLD style, PRIMARY color β component name (e.g., `"UI Icon"`)
- `meta` text β BODY style, MUTED color β `"prop = value, prop = value"` for the props in use
The thumbnail makes the reference instant β no mental translation from a prop string to a visual.
**Between rows:** 1px hairline divider (Rectangle, FILLΓ1, fill `#EAEAEA`).
Omit this card entirely if no subcomponent instances are found.
---
## FOOTER
A small metadata strip at the bottom of the wrap. Tells readers when the doc was generated so they can gauge freshness.
**Layout** (VERTICAL, 4px gap, FILLΓHUG, separated from the data column by the wrap's 58px gap):
- `generated` text β BODY style, MUTED color β `"Generated YYYY-MM-DD via /component-documentation"`
- `last-edited` text β BODY style, MUTED color β `"Component last edited YYYY-MM-DD"` β only include if `compSet.lastModified` or equivalent is available, otherwise omit this line.
---
## Step 5 β Build incrementally
Per figma-pilot rules: **at most ~10 logical operations per `figma_execute` call.** Split building across multiple calls:
1. **Skeleton call** β create outer wrapper (white fill, 80 padding, 24 radius) + description frame (940 wide) + data frame (940 wide) + footer frame as empty placeholders. **No documentation horizontal column. No clone.** Set `placeholder = true` on each. Return all created IDs.
2. **Description call** β populate name + subtitle + description text. Description body must be β€2 sentences focused on purpose β no variant lists, no property lists, no marketing voice.
3. **VARIANTS card call(s)** β build the card heading + variant entries with hairlines between rows. Use visual+intent variant descriptions (color/glyph cue first, semantic intent second). Wire in real usages from Step 2 β omit the "Used in" line if none exist.
4. **ANATOMY card call** β build the card (skip if primitive). Pull direct named children of the most-decorated variant.
5. **PROPERTIES card call** β one `property` block per property (3 stacked text rows each, defaults always shown), with hairline dividers between rows.
6. **COLORS card call** β build grouped by variable folder (skip if empty).
7. **SUBCOMPONENTS card call** β build with thumbnails (skip if empty).
8. **Footer call** β populate generation date + (optionally) component last edited.
9. **Final call** β set sizing modes (`layoutSizingHorizontal = 'FILL'` after parenting), reset `placeholder = false` everywhere, take a screenshot.
**Always return created node IDs from every call** so subsequent calls can reference them.
---
## Reusable helper code
Adaptive helpers β fall back to raw font/color values when the file has no design system. Inline these inside each `figma_execute` call that creates nodes:
- `t.textStyleId = id` is deprecated β use `await t.setTextStyleIdAsync(id)`
- Load font BEFORE setting `t.characters`, even when applying a style afterwards
- `resize()` overrides `primaryAxisSizingMode` β set `'AUTO'` AFTER calling `resize()`
- `layoutSizingHorizontal/Vertical = 'FILL'` only works AFTER `parent.appendChild(child)`
- `figma.currentPage = page` throws β use `await figma.setCurrentPageAsync(page)`
- Colors are 0β1 range, not 0β255
- Position new top-level nodes away from (0,0) β page-level placement defaults to origin and overlaps existing content
- Failed `figma_execute` scripts are atomic β no partial state. Read the error, fix the script, retry.
- `console.log()` is not returned β use `return` for output
---
## Writing good descriptions
If `setDescription` from Step 2 is non-empty, use it verbatim and skip auto-writing.
If empty, auto-write following these rules:
- **β€2 sentences. Hard cap.** First sentence = purpose. Second sentence = primitive/composite note (only if it adds info).
- Don't enumerate variants ("informational, error, warning, success" etc.) β already shown in subtitle and VARIANTS card.
- Don't list properties ("Toggleable Title, Left Icon, CTAβ¦") β already shown in PROPERTIES card.
- No marketing voice ("communicate context to the user", "scale from a quiet inline note to a full action-bearing alert"). Plain, direct.
- Present tense, no filler adjectives.
**Subtitle format:** `"Component Set Β· N variants"` for sets, `"Component Β· 1 variant"` for single components. Center-dot separator, never em-dash.
**Variant descriptions** (per entry in VARIANTS card):
- One short sentence stating what the variant does or when to use it. That's it.
- No filler adjectives like "calm", "neutral", "warm", "approachable" β they read as AI-padded.
- Good: "Communicates a blocking failure."
- Bad: "Communicates a blocking failure or destructive condition the user must resolve before continuing in a clear, urgent tone."
---
## When to skip a section
- No fills found β skip COLORS card
- No nested instances β skip SUBCOMPONENTS card
- No `componentPropertyDefinitions` (single component) β skip VARIANTS card, document as single-state
- Always include PROPERTIES card if the component has properties
---
## Post-creation checklist
1. `figma_capture_screenshot` on the doc frame (plugin-based, immediate β captures fresh state)
2. Verify all cards visible, no clipped text
3. Verify the `data` frame height is HUG (not stuck at 10px β sign that `primaryAxisSizingMode` wasn't reset after `resize()`)
4. Verify the wrap has a white fill (not transparent) β black text on a transparent wrap renders invisible on dark-canvas pages
5. Verify variant rows used HORIZONTAL layout where the inner card width allowed it (only fall back to VERTICAL stack for components too wide to sit beside their description)
6. Verify auto-written description is β€2 sentences and doesn't enumerate variants/properties
7. Verify "Used in: β¦" lines only appear where Step 2 found real usages (no invented placeholders)
8. If text shows wrong fonts/colors, the Step 3 token discovery picked wrong candidates β print the resolved `ST` and `VAR` maps and let the user override specific tokens by name before re-running
---
## Token discovery override
If auto-discovery picks wrong tokens (e.g., file has both a marketing and a product type system, picked the wrong one), let the user override by name:
Always print the resolved token map before building so the user can sanity-check it.
---
## Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `figma_get_status` errors | Figma desktop not running with debug port, or Desktop Bridge plugin not active | Run `open -a 'Figma' --args --remote-debugging-port=9222`, then open Plugins β Desktop Bridge |
| `No cloud relay session` error | Session is using cloud-relay figma-console instead of local | Restart Claude Code to surface the local MCP, or run `figma_pair_plugin` to use cloud relay |
| `Cannot read properties of null` for `getNodeByIdAsync` | Wrong page, or node was deleted | Verify page context with `figma.setCurrentPageAsync`, re-fetch ID from a stable parent |
| Text shows in wrong font | `loadFontAsync` not called before `t.characters = ...` | Always load font first |
| Frame stays at 10px height | `primaryAxisSizingMode` set before `resize()` | Set `'AUTO'` AFTER resize() |
| Doc panels appear collapsed | `layoutSizingHorizontal = 'FILL'` set before `appendChild` | Append first, then set FILL |
| `figma.currentPage = page` throws | Sync setter not supported | Use `await figma.setCurrentPageAsync(page)` |