Guides work on the Figma I/O package (@grida/io-figma, packages/grida-canvas-io-figma/). Covers the fig-kiwi binary parser, Kiwi→REST→Grida conversion pipeline, fig2grida CLI, REST API JSON conversion, and testing with clipboard/fig/REST fixtures. Use when adding node type support, fixing conversion bugs, extending fig2grida, working on the fig-kiwi parser, writing tests for Figma import, or debugging clipboard paste failures after a Figma update.
77
97%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
@grida/io-figmaPackage: packages/grida-canvas-io-figma/
.fig bytes / HTML clipboard
→ fig-kiwi parser (fig-kiwi/) low-level, zero-opinion
→ NodeChange[] (Kiwi schema types)
→ iofigma.fromKiwi*() (lib.ts) Kiwi → Grida node
Figma REST API JSON
→ iofigma.fromRest*() (lib.ts) REST → Grida node
Orchestration:
fig2grida-core.ts — browser-safe: input detection, page loop, pack
fig2grida.ts — CLI wrapper (Node.js only, uses fs + process.argv)Key invariant: The Kiwi path converts to REST format first (Kiwi → REST → Grida). lib.ts is the single source of truth for node conversion; it does not know the input origin.
Output: Grida format (.grida ZIP — FlatBuffers + images). See io-grida skill for format details, Rust loading, and round-trip testing.
fig2grida(input) in fig2grida-core.ts auto-detects the input:
| Input | Detection | Path |
|---|---|---|
.fig bytes | ZIP without document.json, or raw Kiwi | fig-kiwi parser |
| REST archive ZIP | ZIP containing document.json (+ optional images/) | REST JSON path |
| REST JSON bytes | Starts with { | REST JSON path |
| REST JSON object | Non-Uint8Array object | REST JSON path |
The REST JSON path (extractCanvases) accepts multiple response shapes:
{ document: { type: "DOCUMENT", children: [CANVAS, …] } } — full GET /v1/files/:key{ document: { type: "CANVAS", children: […] } } — single-page node fetch{ nodes: { "id": { document: … }, … } } — GET /v1/files/:key/nodes?ids=…{ type: "DOCUMENT", children: … } — document node directly{ type: "CANVAS", children: … } — single CANVAS node{ children: […] } — bare object with childrenPublic APIs (both in fig2grida-core.ts):
fig2grida(input, options?) → .grida ZIP bytes (Fig2GridaResult)restJsonToGridaDocument(json, options?) → in-memory Document + assets (no ZIP packing)| File | Role |
|---|---|
lib.ts | All iofigma.from* converters (Kiwi→REST and REST→Grida) |
fig2grida-core.ts | Orchestrator (.fig, REST JSON, REST ZIP) |
fig2grida.ts | CLI entry point (Node.js only) |
fig-kiwi/index.ts | Low-level parser public API |
fig-kiwi/blob-parser.ts | Vector network + commands blob decoding |
fig-kiwi/schema.ts | Kiwi type definitions (NodeChange, Message, …) |
| Path | What |
|---|---|
.ref/figma/ | Kiwi schema (fig.kiwi, fig.kiwi.d.ts), extraction tool (fig2kiwi.ts), Figma REST & Plugin API typings |
docs/wg/feat-fig/glossary/fig.kiwi.md | Deep-dive: node types, vector blob format, GROUP/FRAME detection, text/font mapping |
packages/grida-canvas-io-figma/README.md | Feature matrix, limitations, usage |
fig-kiwi/schema.ts (Kiwi) or REST JSON in fixtures/test-figma/.lib.ts under the relevant iofigma.from* converter.__tests__/ against an existing fixture.Clipboard issues = Figma changed their Kiwi schema.
readHTMLMessage(html) → inspect raw Message.NodeChange[] against fig-kiwi/schema.ts.schema.ts (field changes) or blob-parser.ts (blob layout changes).pnpm --filter @grida/io-figma fig2grida input.fig
npx tsx packages/grida-canvas-io-figma/fig2grida.ts input.fig --pages 0,2
npx tsx packages/grida-canvas-io-figma/fig2grida.ts input.fig --infofigma_archive.py requires a Figma Personal Access Token. The script
checks FIGMA_TOKEN then X_FIGMA_TOKEN env vars, or accepts
--x-figma-token on the CLI. It fails fast with a clear error if none
is set.
The root .env file is not a standard part of this project — it may
not exist on every machine. Never read .env directly (for security
reasons). Instead, if a token is needed and not already in the
environment, ask the user to provide one and have them export it:
export FIGMA_TOKEN=figd_...Use scripts/figma_archive.py. See the script header for full documentation, output layout, and --export behaviour.
python .agents/skills/io-figma/scripts/figma_archive.py \
--filekey <KEY> --archive-dir fixtures/test-figma/community/<name>
# With oracle PNGs (nodes must have export presets in Figma)
python .agents/skills/io-figma/scripts/figma_archive.py \
--filekey <KEY> --archive-dir fixtures/test-figma/rest-api/local/<name> --exportFor end-to-end correctness of the Figma import pipeline (does our Grida
render of a Figma file match Figma's own render?), use the refig
flow: oracle PNGs from Figma's Images API + @grida/reftest (developed in
the engine repo: https://github.com/gridaco/nothing/tree/main/packages/grida-reftest)
for the diff/score/report. Suites live in the engine repo's gitignored local
corpus (fixtures/local/refig/<name>.<filekey>/ — local-only, machine-local
by definition). See the engine repo's render-reftest skill, section
"Figma — the refig reftest pipeline":
https://github.com/gridaco/nothing/blob/main/.agents/skills/render-reftest/SKILL.md.
When debugging a conversion bug with a visible visual symptom, run the
refig suite to locate the diverging nodes, then drill into lib.ts for
the specific node type or property.
pnpm --filter @grida/io-figma test # all
pnpm --filter @grida/io-figma test -- __tests__/iofigma.kiwi.test.ts # specific| Test file | Covers |
|---|---|
iofigma.kiwi.test.ts | Kiwi clipboard → Grida |
iofigma.kiwi.fig.test.ts | .fig file parsing |
iofigma.kiwi.vector-network.test.ts | Vector network blob decoding |
iofigma.kiwi.clipboard-overrides.test.ts | Component instance overrides |
iofigma.kiwi.clipboard-components.test.ts | Component / instance clipboard |
iofigma.kiwi.clipboard-text-overrides.test.ts | Text style overrides |
iofigma.rest-api.no-geometry.test.ts | REST API (no geometry) |
iofigma.rest-api.vector.test.ts | REST API vector paths |
fig2grida.test.ts | End-to-end pipeline |
fig-kiwi/__tests__/ | Low-level parser units |
Fixtures: fixtures/test-figma/rest-api/ (committed REST JSON), fixtures/test-figma/community/ (archived files), fixtures/local/ (gitignored, manual testing).
characterStyleOverrides / styleOverrideTable partially mapped from KiwiCheck the README's limitations section before writing new code. If lifting a limitation, update the README.
pnpm turbo typecheck --filter='./packages/grida-canvas-io-figma'
pnpm turbo test --filter='./packages/grida-canvas-io-figma'gridaco/grida
9cbacac
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.