Documentation
Everything tofig does, everything it doesn't, and what to do about the difference.
Install
There are two pieces. Most people only need the first.
The Figma plugin
Published to the acaso organization. Open any Design or Slides file and find tofig in your organization's plugins. Nothing else to configure: there's no account and no API key.
The renderer CLI (optional)
Only needed for exports the plugin sandbox can't run. See External renderer for when that applies.
npm install -g @aca-so/tofig
Requires Node and a local Google Chrome or Chromium. puppeteer-core drives your existing browser rather than downloading one.
Quick start
- Open a Figma Design file, or a Figma Slides file.
- Run tofig from the plugins menu.
- Paste your HTML, or drop a single
.htmlfile onto the window. - Click Convert.
tofig reads figma.editorType and picks the output for you:
frames in a Design file, slides in a Slides file. There is no mode switch.
Coming from Claude
The most common route in. Claude generates a design as an artifact, and you want it in Figma as layers rather than a flat screenshot. The full walkthrough is on its own page, including the export prompt and what to do when the artifact turns out to be a live app.
The short version: ask for the design as a single self-contained HTML
file, with inline CSS, no CDN links, nothing rendered by JavaScript at
runtime, images as data: URIs or inline SVG, and a font that
exists in your Figma. Then paste that into the plugin.
Decks work the same way, and are one of the best cases. A Claude slide deck is a single HTML file with one section per slide. Run tofig in a Figma Slides file and each section arrives as its own slide, sized and scaled to fit, with its text still editable.
What tofig accepts
Self-contained HTML. Concretely, that means everything the page needs is in the file:
- Inline
<style>andstyle=""attributes - Images as
data:/ base64 URIs - Inline
<svg> - Web fonts that already exist in your Figma
External URLs are never fetched. The plugin declares
networkAccess: none in its manifest, so Figma blocks outbound
requests entirely. A stylesheet or image behind https:// will
simply be absent. This is deliberate: it's what makes the plugin safe to point
at markup you didn't write.
What you get back
| In your HTML | In Figma |
|---|---|
| Block elements | Frames, clipped to their own bounds |
| Text nodes | Real text layers: editable, restyleable |
Inline <svg> | Vector nodes with editable paths |
data: images | Image fills |
| CSS gradients | Native Figma gradient paints |
| Borders, radii | Strokes and corner radii |
<section> in a deck | One slide, or one frame, per section |
Layout is absolutely positioned. tofig optimises for visual fidelity (what you see is where it lands) rather than reconstructing auto-layout. Auto-layout inference is on the roadmap.
Fonts & substitution
Figma can only set type in fonts it already has. When your HTML asks for one that isn't available, tofig walks a fallback chain:
- The requested family at the requested weight and style.
- The nearest available weight in the same family.
- Inter.
Every substitution is reported back in the plugin UI after conversion, so you know exactly which layers drifted and why. If text looks slightly small after import, that's the fitting pass: a fallback font usually reflows taller, and since every layer is absolutely positioned, tofig shrinks text to stay inside its measured box rather than letting it overlap the element below.
External renderer
Some files with an .html extension are really applications.
They boot a React runtime, pull React or Babel from a CDN, call
eval, or read localStorage. Figma's plugin sandbox
blocks all of that, so they cannot render inside the plugin.
For those, render on your own machine in an unrestricted Chrome and import the result. Same extractor, same output, just no sandbox.
# writes export.tofig.json next to the input
tofig-render export.html
# or without installing anything
npx --package=@aca-so/tofig tofig-render export.html
Then drop the resulting .tofig.json onto the plugin, or use
Choose…. It imports directly, with no in-plugin render step.
Options
| Flag | Default | What it does |
|---|---|---|
--target | design | design or slides |
--width | 1440 | Viewport width used for the render |
--out | alongside input | Output path for the JSON |
--chrome | auto-detected | Path to a specific Chrome binary |
--headful | off | Show the browser, useful when debugging a render |
Chrome is also picked up from TOFIG_CHROME or
CHROME_PATH if either is set.
The .tofig.json file
A plain JSON import payload: the extracted layer tree, already resolved to geometry, styles and embedded image bytes. It's deterministic: the same input produces the same file, which makes it reviewable and diffable.
{
"tofig": 1,
"target": "slides",
"title": "deck",
"multiFrame": true,
"roots": [ /* one entry per frame or slide */ ]
}
Because the payload carries its own bytes, the plugin never needs network access to import it. The security property is preserved end to end.
Troubleshooting
Nothing renders, or the result is blank
The export is probably a live app rather than static markup. Use the external renderer.
Images are missing
They're almost certainly external URLs. Inline them as data:
URIs and re-export.
Text overlaps other text
A font substitution reflowed taller than the original. tofig fits text back into its measured box, but a very tight design can still crowd. Install the real font in Figma and re-run.
The type looks slightly smaller than the original
Expected, and deliberate: see Fonts & substitution. A marginally smaller glyph is the trade for no overlap.
A layer sticks out past its frame
Shouldn't happen as of 0.3.1, which clips each captured root to its own frame. If you see it, please open an issue with the HTML.
Build from source
The repo is public and MIT licensed.
git clone https://github.com/aca-so/tofig
cd tofig
npm install
npm run build # produces code.js + ui.html
Then in the Figma desktop app: Plugins → Development → Import
plugin from manifest… and pick this repo's
manifest.json. Two sample files live in
examples/: one for Design, one for Slides.