nextjs-figma-conversion — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited nextjs-figma-conversion (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Turn a running web app into a native, editable Figma file. The strategy is capture, then reconstruct: there is no automatic code→Figma exporter, so we drive the rendered app to extract exact geometry, then rebuild it in Figma via the Plugin API. This produces real Figma variables, components, and frames — not flat screenshots.
Prerequisites
mcp__plugin_figma_figma__* tools). Ifnot, call its authenticate tool and have the user approve the OAuth URL.
hard requirement and prevents common failures.
npm/pnpm/yarn dev).playwright-core withchannel: 'chrome', avoiding a ~150 MB browser download).
Copy scripts/ into the project (e.g. scripts/figma-export/) and create a figma-export.config.json from assets/figma-export.config.example.json. All scripts read that config (path overridable as argv[2]).
package.json, app//pages/, the globalCSS (design tokens / :root CSS variables), components.json, and the shared components. List every route. For dynamic routes ([id]) find a real id from fixture/seed data. Decide scope with the user: full (tokens + components + all screens), key screens, or tokens-only.
pnpm install then start the dev server in the background.Verify each route returns 200 (curl -o /dev/null -w "%{http_code}").
pnpm add -D playwright-core.node scripts/figma-export/screenshot.mjs.These are your fidelity reference (and what you compare Figma output against).
node scripts/figma-export/extract-layout.mjs.Walks the rendered DOM and emits, per element, absolute x/y/w/h, background, border, radius, opacity, and text (content, color, font size/weight/family). Colors are resolved through a <canvas> so Tailwind v4 oklch/color-mix values come back as sRGB, and alpha is preserved separately.
node scripts/figma-export/flatten.mjs. Converts each DOM treeinto a preorder, absolutely-positioned node list (parents before children → correct paint/z-order when appended flat to one frame).
node scripts/figma-export/gen-figma.mjs.Writes one self-contained use_figma script per route (renderer + inline data), laid out left-to-right on the canvas, each under the 50 KB code limit.
mcp__plugin_figma_figma__create_new_file(editorType design). If the user has multiple plans, ask which team. Make three pages: 🎨 Tokens, 🧩 Components, 📐 Screens.
color-usage scopes (FRAME_FILL, SHAPE_FILL, TEXT_FILL, STROKE_COLOR); radii are FLOAT with scope CORNER_RADIUS. Bind each to a swatch so it doubles as a living reference. See references/figma-build.md.
list rows, panels) as variant sets (combineAsVariants) bound to the token variables. SVG logos/icons import cleanly via figma.createNodeFromSvg — substitute currentColor with a real hex in the string first.
code arg to use_figma. The renderer is idempotent (it removes any existing frame of the same name first), so re-running a single screen is safe.
get_screenshot, compare to the reference PNG, fix the renderer if needed, regenerate, re-run. The demo/first screen is the canary: once it matches, the rest use the identical renderer.
Read references/figma-gotchas.md for the full list. The highest-impact ones:
getComputedStyle returns themliterally; naive RGB parsing yields garbage (e.g. a primary/10 tint becomes #010000). The extractor resolves every color via a 1×1 canvas → sRGB.
hex hides its own text. Capture alpha separately and set it as the Figma paint's opacity (and capture element opacity too).
opacity on the returnedpaint, not the input: {...figma.variables.setBoundVariableForPaint(p,'color',v), opacity: o}.
direct text (buttons, badges, chips) must produce a frame and a text node, or you lose the background. Center the text within the box.
slightly wider than the browser's, so a fixed-width single-line label wraps. Use WIDTH_AND_HEIGHT for single lines (heuristic: h <= fontSize*1.7) and HEIGHT (fixed width) only for true multi-line text.
border-radius: 9999px arrives as a huge number; clamp tomin(r, w/2, h/2).
(compact JSON, short keys); split very large screens across calls if needed.
throws. Set it at most once per call.
Only read tools (get_screenshot, get_metadata, get_design_context, get_variable_defs) count against limits. Write tools (use_figma, create_new_file, whoami) are exempt. Caps by plan/seat:
| Seat | Starter | Professional | Organization | Enterprise |
|---|---|---|---|---|
| View / Collab | 6/mo | 6/mo | 6/mo | 6/mo |
| Dev / Full | 6/mo | 200/day · 10/min | 200/day · 15/min | 600/day · 20/min |
Implication: spend screenshots sparingly. On Starter you get ~6 reads/month — verify the canary screen, then trust the validated renderer for the rest. You can keep building/editing freely because writes don't count.
scripts/ — _config.mjs, screenshot.mjs, extract-layout.mjs,flatten.mjs, gen-figma.mjs.
references/figma-build.md — variable/component/screen build details & snippets.references/figma-gotchas.md — every known pitfall with the fix.assets/figma-export.config.example.json — config template.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.