Academic Figure in Figma (Claude Code)
Land a sketch, candidate image, or text description as an editable, paper-exact
vector figure in Figma, drawn directly through the Figma MCP server.
Draw from this skill alone. references/figma-api-cheatsheet.md +
scripts/figma_lib.js contain the complete verified API subset — do NOT load the
official figma-use skill, the Plugin API typings, or explore the API by trial: that
is the slow path this skill replaces. Icons come from the local cache first
(scripts/assets/icons/icons.json, pre-cleaned, injection-ready).
Hard rules
- Correctness before aesthetics. Grep the paper for every number, term, metric
name, and section reference the figure will carry; show the audit table before
drawing. Fix the reference image's errors, never reproduce them. Terminology must
match the paper canon verbatim; when the paper is inconsistent, ask the user to
pick. Never hard-code section numbers into a figure.
This binds the notation too. Grep the live source for every symbol before drawing
it: the paper that writes
x^\ast 31 times and x^\star never must not get a star in
its figure, and a symbol the paper never uses (x_1 where it always writes \hat x_1
or \hat x) is an error even though it looks right. An algorithm's local name is fair
game when the prose symbol will not fit — label the panel d and gloss
d=\operatorname{diag}(A^\top A) in the caption — but only if the paper itself uses d.
- Measure the render, never the node tree. Gutters, ink coverage and font size are
properties of pixels, and every taste argument is settled by
uv run --with pymupdf --with pillow --with numpy python scripts/measure_figure.py <fig.pdf>,
which also measures a reference paper's figure with --figure N. A number read off a
caption's text block, a node's bounding box or a 1x screenshot is not a measurement:
one such reading put a 19 pt gutter into the style contract and left four figures
visibly inset until the user caught it by eye. Run it on every figure before handing
it over, and on two reference figures whenever a new rule is being set.
- Basic building blocks only. Frame, Text, Line, Polygon, SVG import, absolute
x/y, plus the component-reuse subset (createComponentFromNode / createInstance).
Auto Layout, variant sets, Variables, Styles are banned (cheatsheet §Allowed).
- Design at final print size (references/paper-canvas-specs.md). Never draw big
and shrink — fonts fall below the 6pt floor.
- Figure grammar (references/figure-grammar.md): evidence for every arrow, no
false relays, variables on edges not boxes, operation chains not just outcomes,
repeated entities compressed, mainline centred, restrained palette.
- Logo semantics + eye check. Base-model logos on the backbone block only; never
a brand mark on the proposed-model block. Screenshot every fetched logo before use
— CDNs mislabel (cache manifest records which marks are already verified).
- Say what you expect, change it, then let the canvas say what changed (the VISTA
loop, cheatsheet §Look, then say what changed). Before a mutating call, one line of
expected outcome in the
description. Inside the call: const before = snapshot(art)
first, diffLayout(before, snapshot(art)) in the return value, and
await inspect(art, question, region?) — a print-scale (8×) view of the artboard or
of one region, with the visual question it must answer. get_screenshot never
upscales, and a 1× render of a 236 pt figure hides every collision and padding defect
the reader will see (cheatsheet §Core facts). Check: text overflow, single-headed
arrows pointing with the flow, whitespace balance, terminology.
- One element, one node; one figure, one style table. Arrows are single
vectorNetwork nodes (never line+polygon fragments). Same-kind elements are
generated from one data table with STYLE tokens; end every session with
auditFigure() — structured lint for font floor, ink overflow, block
overlap, arrow-through-block, buried heads and style drift — and fix every
ERROR before asking the user to review.
Chip heights are outputs, not inputs. Size every chip from its text ink
via fitChipToInk() / packBox() (lib) so text can never overflow or be
occluded; hand-guessed fixed heights are banned, and any text change means
re-running packBox on that box before re-laying arrows — in a fresh
call: text metrics are stale within the call that edited the text, so
same-call packing and linting silently pass on real overlaps
(cheatsheet §Text fitting, stale-metrics trap).
- Two faces, fixed: words in Arimo 6.5 pt, symbols in Computer Modern 8 pt at 1:1.
Every symbol —
x_t, a fraction, a norm, a tick numeral — goes through
latex2svg.py (fontset cm, the body's maths font) and is placed with symbol()
or cloned from the masters-cm frame, never rescaled (cheatsheet §Formulas,
style-contract §Type: measured on MoCo, MAE, iMF, JiT, BNF). mathText() only for
plain sub/superscripts inside a prose label. User-made formula components are
reused via findAll + createInstance, never redrawn.
- Show the method's own data, not named rectangles. Where a quantity in the
figure is an image, put the real one there:
use_figma cannot create a bitmap,
so the panels are frames whose fills arrive through the upload_assets MCP tool
(cheatsheet §Raster panels). Render each panel natively from the source script,
crop to the frame's aspect before upload, and upsample to 4.2 × pt pixels —
NEAREST for masks, LANCZOS for everything else.
- Draw to the measured contract, and clone every symbol.
references/style-contract.md
holds the numbers accepted figures actually use: one 6.5 pt prose size, stroke weights
0.3 / 0.5 / 0.9 and nothing between, no stroke on a fill that sits on white, ink spanning
the 236 pt frame with gutters of at most 10 pt, coverage 20-40 %, at most two tinted
blocks. A typeset sym-<key> is a single
layer, so park the uploads once in a masters-typeset frame and place master.clone()
every time — appendChild on the master moves it, which silently strips the label from
whichever figure held it before.
- Reference colours are measured, not guessed. When a reference image exists,
read component colours with
scripts/extract_palette.py (cheatsheet
§Reference colours): crop mode returns one component's fill/stroke/text trio,
probe mode the exact colour at a point. Override the lib PAL with the
measured hexes before drawing; same-role components share one measured colour.
- The figure's memory lives on disk, not in the conversation. After every wave
write
guideTable(art) (lib) into figs/<figure>/GUIDE.md — ids, names, coordinates,
fonts, fills — together with the decisions taken (why a label sits where it sits,
what the user rejected), and save every render as renders/<figure>_v<NN>.png,
never overwriting. A compacted context, or tomorrow's session, resumes from that
file in one read instead of re-deriving the layout from the canvas.
Workflow
First run / connection problems — guided setup. If Figma MCP tools are missing,
auth fails, or whoami shows a View seat, switch to the five-step tutorial in
references/figma-mcp-setup.md (§Tutorial): Claude leads, verifies each step, and
returns here when drawing-ready.
Step 0 — Preflight. whoami (quota-exempt) → seat must be Full with a paid or
education plan, else run the setup tutorial first (references/figma-mcp-setup.md). Pick canvas width
from the venue (references/paper-canvas-specs.md). Produce the Step-0 correctness
audit table (rule 1) and the figure-grammar plan (rule 5). Read
scripts/figma_lib.js and the icon cache manifest now — every later call pastes the
lib verbatim at the top of its code.
Step 1 — Skeleton (1 call). Artboard at print width + all stage/panel containers
via stageColumn(); panel titles, dashed divider. Return every container id.
Step 2 — Parallel fill (N calls, one message). One call per container, following
references/parallel-drawing.md: paste lib, await FONTS(), fill chips/text/icons for
that container only (chip(), txt(), placeSvg() with cached icons). Fan out all
containers simultaneously; never touch siblings or globals.
Step 3 — Assembly (1 call). Arrows between stages (arrowH/arrowV, colour per
flow type), legend (legendRow), per-column balance (balanceColumn), artboard trim.
Keep the returned arrow ids for later adjustments — never re-find arrows by type.
Step 4 — Review loop. Screenshot at 2.5-3x AND re-read the reference image
side-by-side (structure being right is not enough — compare density, spacing,
line routing against the original). Run rule 7, auditConsistency(), plus the
error vocabulary at the end of references/figure-grammar.md. Independent fixes may fan out again.
Stop when clean; ask the user to review in Figma at 100% zoom; user exports PDF — run the
font-compliance check (references/build-workflow.md §Venue font compliance) before handing it over.
References
| File |
Load when |
| references/figma-api-cheatsheet.md |
before writing the first use_figma call (always) |
| references/parallel-drawing.md |
at Step 2 |
| references/paper-canvas-specs.md |
at Step 0 (canvas + fonts + palette numbers) |
| references/figure-grammar.md |
at Step 0 planning and Step 4 review |
| references/figure-styles.md |
picking or switching a figure's style/palette: named presets, construction rules, sampler-board + hex-map workflow |
| references/style-contract.md |
at Step 0 for the numbers (palette, type, strokes, spacing, banned tells) and at Step 4 as the checklist; measured from five He Kaiming figures and three flagship inverse-problem papers |
| references/icon-sourcing.md |
only when an icon is NOT in the local cache |
| references/figma-mcp-setup.md |
connection/quota problems only |
| references/build-workflow.md |
deep dives: balancing math, pitfalls, export + venue font compliance |
1---2name: academic-figure-figma3description: Use when the user wants a paper figure (framework, pipeline, architecture, method overview) drawn, rebuilt, or refined as an editable vector in Figma via Claude Code — including reproducing a candidate/reference image (e.g. studio-pro C01-C04) at exact print size. Also use when the user needs to set up or fix the Claude Code-Figma connection: installing the Figma MCP plugin, OAuth/authentication (incl. remote sessions), seat or quota problems, "figma 连不上/怎么配置", or a first-run guided setup.4---56# Academic Figure in Figma (Claude Code)78Land a sketch, candidate image, or text description as an **editable, paper-exact9vector figure in Figma**, drawn directly through the Figma MCP server.1011**Draw from this skill alone.** `references/figma-api-cheatsheet.md` +12`scripts/figma_lib.js` contain the complete verified API subset — do NOT load the13official figma-use skill, the Plugin API typings, or explore the API by trial: that14is the slow path this skill replaces. Icons come from the local cache first15(`scripts/assets/icons/icons.json`, pre-cleaned, injection-ready).1617## Hard rules18191. **Correctness before aesthetics.** Grep the paper for every number, term, metric20 name, and section reference the figure will carry; show the audit table before21 drawing. Fix the reference image's errors, never reproduce them. Terminology must22 match the paper canon verbatim; when the paper is inconsistent, ask the user to23 pick. Never hard-code section numbers into a figure.24 **This binds the notation too.** Grep the live source for every symbol before drawing25 it: the paper that writes `x^\ast` 31 times and `x^\star` never must not get a star in26 its figure, and a symbol the paper never uses (`x_1` where it always writes `\hat x_1`27 or `\hat x`) is an error even though it looks right. An algorithm's local name is fair28 game when the prose symbol will not fit — label the panel `d` and gloss29 `d=\operatorname{diag}(A^\top A)` in the caption — but only if the paper itself uses `d`.302. **Measure the render, never the node tree.** Gutters, ink coverage and font size are31 properties of pixels, and every taste argument is settled by32 `uv run --with pymupdf --with pillow --with numpy python scripts/measure_figure.py <fig.pdf>`,33 which also measures a reference paper's figure with `--figure N`. A number read off a34 caption's text block, a node's bounding box or a 1x screenshot is not a measurement:35 one such reading put a 19 pt gutter into the style contract and left four figures36 visibly inset until the user caught it by eye. Run it on every figure before handing37 it over, and on two reference figures whenever a new rule is being set.383. **Basic building blocks only.** Frame, Text, Line, Polygon, SVG import, absolute39 x/y, plus the component-reuse subset (createComponentFromNode / createInstance).40 Auto Layout, variant sets, Variables, Styles are banned (cheatsheet §Allowed).414. **Design at final print size** (references/paper-canvas-specs.md). Never draw big42 and shrink — fonts fall below the 6pt floor.435. **Figure grammar** (references/figure-grammar.md): evidence for every arrow, no44 false relays, variables on edges not boxes, operation chains not just outcomes,45 repeated entities compressed, mainline centred, restrained palette.466. **Logo semantics + eye check.** Base-model logos on the backbone block only; never47 a brand mark on the proposed-model block. Screenshot every fetched logo before use48 — CDNs mislabel (cache manifest records which marks are already verified).497. **Say what you expect, change it, then let the canvas say what changed** (the VISTA50 loop, cheatsheet §Look, then say what changed). Before a mutating call, one line of51 expected outcome in the `description`. Inside the call: `const before = snapshot(art)`52 first, `diffLayout(before, snapshot(art))` in the return value, and53 `await inspect(art, question, region?)` — a print-scale (8×) view of the artboard or54 of one region, with the visual question it must answer. `get_screenshot` never55 upscales, and a 1× render of a 236 pt figure hides every collision and padding defect56 the reader will see (cheatsheet §Core facts). Check: text overflow, single-headed57 arrows pointing with the flow, whitespace balance, terminology.588. **One element, one node; one figure, one style table.** Arrows are single59 vectorNetwork nodes (never line+polygon fragments). Same-kind elements are60 generated from one data table with STYLE tokens; end every session with61 `auditFigure()` — structured lint for font floor, ink overflow, block62 overlap, arrow-through-block, buried heads and style drift — and fix every63 ERROR before asking the user to review.64 **Chip heights are outputs, not inputs.** Size every chip from its text ink65 via `fitChipToInk()` / `packBox()` (lib) so text can never overflow or be66 occluded; hand-guessed fixed heights are banned, and any text change means67 re-running `packBox` on that box before re-laying arrows — **in a fresh68 call**: text metrics are stale within the call that edited the text, so69 same-call packing and linting silently pass on real overlaps70 (cheatsheet §Text fitting, stale-metrics trap).719. **Two faces, fixed: words in Arimo 6.5 pt, symbols in Computer Modern 8 pt at 1:1.**72 Every symbol — `x_t`, a fraction, a norm, a tick numeral — goes through73 `latex2svg.py` (fontset `cm`, the body's maths font) and is placed with `symbol()`74 or cloned from the `masters-cm` frame, never rescaled (cheatsheet §Formulas,75 style-contract §Type: measured on MoCo, MAE, iMF, JiT, BNF). `mathText()` only for76 plain sub/superscripts inside a prose label. User-made formula components are77 reused via `findAll` + `createInstance`, never redrawn.7810. **Show the method's own data, not named rectangles.** Where a quantity in the79 figure is an image, put the real one there: `use_figma` cannot create a bitmap,80 so the panels are frames whose fills arrive through the `upload_assets` MCP tool81 (cheatsheet §Raster panels). Render each panel natively from the source script,82 crop to the frame's aspect before upload, and upsample to `4.2 × pt` pixels —83 NEAREST for masks, LANCZOS for everything else.8411. **Draw to the measured contract, and clone every symbol.** `references/style-contract.md`85 holds the numbers accepted figures actually use: one 6.5 pt prose size, stroke weights86 0.3 / 0.5 / 0.9 and nothing between, no stroke on a fill that sits on white, ink spanning87 the 236 pt frame with gutters of at most 10 pt, coverage 20-40 %, at most two tinted88 blocks. A typeset `sym-<key>` is a single89 layer, so park the uploads once in a `masters-typeset` frame and place `master.clone()`90 every time — `appendChild` on the master moves it, which silently strips the label from91 whichever figure held it before.9212. **Reference colours are measured, not guessed.** When a reference image exists,93 read component colours with `scripts/extract_palette.py` (cheatsheet94 §Reference colours): crop mode returns one component's fill/stroke/text trio,95 probe mode the exact colour at a point. Override the lib `PAL` with the96 measured hexes before drawing; same-role components share one measured colour.9713. **The figure's memory lives on disk, not in the conversation.** After every wave98 write `guideTable(art)` (lib) into `figs/<figure>/GUIDE.md` — ids, names, coordinates,99 fonts, fills — together with the decisions taken (why a label sits where it sits,100 what the user rejected), and save every render as `renders/<figure>_v<NN>.png`,101 never overwriting. A compacted context, or tomorrow's session, resumes from that102 file in one read instead of re-deriving the layout from the canvas.103104## Workflow105106**First run / connection problems — guided setup.** If Figma MCP tools are missing,107auth fails, or whoami shows a View seat, switch to the five-step tutorial in108references/figma-mcp-setup.md (§Tutorial): Claude leads, verifies each step, and109returns here when drawing-ready.110111**Step 0 — Preflight.** `whoami` (quota-exempt) → seat must be Full with a paid or112education plan, else run the setup tutorial first (references/figma-mcp-setup.md). Pick canvas width113from the venue (references/paper-canvas-specs.md). Produce the Step-0 correctness114audit table (rule 1) and the figure-grammar plan (rule 5). Read115`scripts/figma_lib.js` and the icon cache manifest now — every later call pastes the116lib verbatim at the top of its code.117118**Step 1 — Skeleton (1 call).** Artboard at print width + all stage/panel containers119via `stageColumn()`; panel titles, dashed divider. Return every container id.120121**Step 2 — Parallel fill (N calls, one message).** One call per container, following122references/parallel-drawing.md: paste lib, `await FONTS()`, fill chips/text/icons for123that container only (`chip()`, `txt()`, `placeSvg()` with cached icons). Fan out all124containers simultaneously; never touch siblings or globals.125126**Step 3 — Assembly (1 call).** Arrows between stages (`arrowH`/`arrowV`, colour per127flow type), legend (`legendRow`), per-column balance (`balanceColumn`), artboard trim.128Keep the returned arrow ids for later adjustments — never re-find arrows by type.129130**Step 4 — Review loop.** Screenshot at 2.5-3x AND re-read the reference image131side-by-side (structure being right is not enough — compare density, spacing,132line routing against the original). Run rule 7, `auditConsistency()`, plus the133error vocabulary at the end of references/figure-grammar.md. Independent fixes may fan out again.134Stop when clean; ask the user to review in Figma at 100% zoom; user exports PDF — run the135font-compliance check (references/build-workflow.md §Venue font compliance) before handing it over.136137## References138139| File | Load when |140|---|---|141| references/figma-api-cheatsheet.md | before writing the first use_figma call (always) |142| references/parallel-drawing.md | at Step 2 |143| references/paper-canvas-specs.md | at Step 0 (canvas + fonts + palette numbers) |144| references/figure-grammar.md | at Step 0 planning and Step 4 review |145| references/figure-styles.md | picking or switching a figure's style/palette: named presets, construction rules, sampler-board + hex-map workflow |146| references/style-contract.md | at Step 0 for the numbers (palette, type, strokes, spacing, banned tells) and at Step 4 as the checklist; measured from five He Kaiming figures and three flagship inverse-problem papers |147| references/icon-sourcing.md | only when an icon is NOT in the local cache |148| references/figma-mcp-setup.md | connection/quota problems only |149| references/build-workflow.md | deep dives: balancing math, pitfalls, export + venue font compliance |