npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@magicpatterns/html-to-figma

v1.0.4

Published

Convert a live HTML subtree into a Figma clipboard payload that pastes losslessly into Figma.

Readme

@magicpatterns/html-to-figma

Converts a live DOM subtree into the binary clipboard payload Figma reads on paste. Runs in the browser — you hand it the element you want exported, it returns a text/html string you can write to the system clipboard, and pasting into Figma materialises the tree as real Figma nodes (frames, text, vectors, images).

How it works

Two-step flow:

  1. inlineComputedStyles(root) — must run in a browser with the element mounted. Walks the subtree, reads computed styles from the live layout, stamps each element's inline style attribute, and records its rendered rect as data-rect="x,y,w,h". Returns the serialised HTML string. This snapshot is what the next step consumes — it doesn't need the live layout engine anymore.

  2. generateFromElement(cloneRoot, { topLayerName }) — parses the inlined clone, preloads referenced fonts and images in parallel, builds a Figma node tree, encodes the fig-kiwi binary envelope, and returns the text/html string. Write it to the clipboard under the text/html MIME:

    const html = await generateFromElement(root, { topLayerName: 'MyExport' })
    await navigator.clipboard.write([
      new ClipboardItem({ 'text/html': new Blob([html], { type: 'text/html' }) }),
    ])

Pressing ⌘V in Figma produces the node tree.

Install & use

yarn add @magicpatterns/html-to-figma

New API (preferred)

import {
  generateFromElement,
  inlineComputedStyles,
} from '@magicpatterns/html-to-figma'

const root = document.getElementById('to-export')!
// Snapshot computed styles + rects from the live DOM.
const inlined = inlineComputedStyles(root)
// Reparse so the generator works on a clean, detached DOM.
const clone = new DOMParser()
  .parseFromString(inlined, 'text/html')
  .body.firstElementChild!
const payload = await generateFromElement(clone, {
  topLayerName: 'MyExport',
})
await navigator.clipboard.write([
  new ClipboardItem({ 'text/html': new Blob([payload], { type: 'text/html' }) }),
])

Exporting multiple screens at once

When a canvas-style UI wants to paste several designs into Figma with their relative positions preserved, call generateFromElements instead of combining payloads by hand. It composes one clipboard archive with a single wrapper FRAME containing each screen at its given {x, y}. Fonts, images, and vector blobs are de-duplicated across screens.

import {
  generateFromElements,
  inlineComputedStyles,
} from '@magicpatterns/html-to-figma'

// Ask each iframe / node for its inlined HTML, parse each back to an
// Element, then collect into a ScreenSpec[].
const screens = selectedNodes.map((node) => {
  const inlined = inlineComputedStyles(node.exportRoot)
  const root = new DOMParser()
    .parseFromString(inlined, 'text/html')
    .body.firstElementChild!
  return { root, x: node.x - minX, y: node.y - minY, name: node.name }
})

const payload = await generateFromElements(screens, { name: 'My Canvas' })
await navigator.clipboard.write([
  new ClipboardItem({ 'text/html': new Blob([payload], { type: 'text/html' }) }),
])

Legacy API (for migration)

The original htmlToFigma export is still available and returns the same intermediate JSON tree it always has. It's maintained for backwards-compatibility while callers migrate to generateFromElement. New code should use the new API.

import { htmlToFigma } from '@magicpatterns/html-to-figma'

const tree = htmlToFigma(document.getElementById('to-export')!)
// ... existing consumer code handles `tree`

Public API surface

| Export | Kind | Purpose | |---|---|---| | generateFromElement | async fn | Preferred. Returns the Figma clipboard text/html payload for one DOM subtree. | | generateFromElements | async fn | Multi-screen variant: composes N subtrees into one payload with each at its given {x, y}. | | inlineComputedStyles | fn | Snapshot live-DOM computed styles + rects into an HTML string. | | GenerateOptions | type | Options shape for generateFromElement. | | GenerateManyOptions | type | Options shape for generateFromElements. | | ScreenSpec | type | Shape of each screen passed to generateFromElements. | | htmlToFigma | fn | Legacy. Synchronous intermediate-JSON export. |

Development

Running the playground

The repo ships a Vite app at src/dev/ with example components and a "Copy for Figma paste" button. Use it to try out changes end-to-end before shipping — ⌘V into Figma after copying.

yarn dev           # starts Vite on http://localhost:5173

Drop new example components into src/dev/ExampleComponents/ and wire them into src/dev/App.tsx's EXAMPLES array.

Building the library

yarn build         # vite build → dist/htmlToFigma.{js,umd.cjs}
                   # then tsc -p tsconfig.build.json → dist/*.d.ts
yarn preview       # serves the built bundles for smoke-testing

vite.config.ts marks opentype.js, kiwi-schema, and pako as external — the consumer's bundler resolves them from node_modules so we don't duplicate runtime deps.

Linting

yarn lint

Debugging scripts

Node-only scripts under scripts/ help inspect the binary format:

| Script | What it does | |---|---| | yarn generate-fixture <slug> | Reads fixtures/<slug>.input.html, runs the generator, writes fixtures/<slug>.generated.html. pbcopy < fixtures/<slug>.generated.html to paste-test. | | yarn decode-fixture <slug> | Parses an archive and dumps the JSON node tree. Great for seeing what the canonical Figma output produces. | | yarn decode-blob <slug> <n> | Decodes a single glyph/vector blob out of an archive's blob table. | | yarn extract-schema <slug> | Pulls the embedded kiwi schema from a fixture and rewrites src/lib/figmaSchema.ts. Only needed when Figma bumps the clipboard schema. | | yarn roundtrip-fixture <slug> | Decodes and re-encodes a fixture, verifying our archive reader/writer stays byte-reversible. | | yarn test-fixture <slug> | Runs the generator against a fixture's saved input HTML. | | yarn verify-generated <slug> | Sanity-checks a generated payload parses and has no obvious schema violations. |

Project layout

src/lib/           ★ everything that ships
src/dev/           ✗ Vite playground (never published)
fixtures/          ✗ captured input + reference HTML pairs (never published)
scripts/           ✗ debug utilities (never published)
assets/            ✗ bundled Inter TTFs for Node-side tests (never published)
.reference/        ✗ committed reference material, not published
dist/              ★ build output (gitignored, generated by `yarn build`)

Three layers keep non-ship files out of published tarballs:

  1. package.json "files": ["dist"] — npm only publishes allowlisted entries. Everything else is already invisible to npm publish.
  2. .npmignore — explicit deny list as a second pass.
  3. prepublishOnly script — runs yarn build and asserts .reference/ did not leak into dist/ before publish proceeds.

Verify what would ship with npm pack --dry-run.

Publishing

TL;DR

# from packages/html-to-figma/
# 1. bump the version
npm version patch      # or: minor | major | <exact-version>
# 2. preview what will ship
npm pack --dry-run
# 3. publish
npm publish --access restricted

That's it. You do NOT need to run yarn build manually — the prepublishOnly hook runs it automatically and verifies the output before anything hits the registry.

Step-by-step

  1. Make sure main is clean and yarn is up to date.

    git status          # nothing uncommitted
    yarn install        # if package.json dependencies changed recently
  2. Bump the version. npm version edits package.json and creates a version-bump commit + git tag in one go.

    npm version patch        # 0.0.36 → 0.0.37
    # npm version minor      # 0.0.36 → 0.1.0
    # npm version major      # 0.0.36 → 1.0.0
    # npm version 0.1.2      # exact
  3. Preview the tarball contents. Confirms only dist/ + README.md

    • package.json ship — no fixtures/, no .reference/, no scripts/.
    npm pack --dry-run
  4. Publish.

    npm publish --access restricted

    What happens under the hood when you run publish:

    1. npm fires the prepublishOnly hook, which runs:
      • yarn build (clean rebuild of dist/)
      • a sanity check that dist/htmlToFigma.js exists
      • a sanity check that dist/.reference/ did not leak in
    2. npm packs the allowlisted files ("files": ["dist"] in package.json, plus README.md + package.json by default).
    3. npm uploads the tarball to the registry.

    If prepublishOnly throws, fix the underlying issue and retry — don't npm publish --force past it. The checks exist to prevent broken or leaky releases.

  5. Push the version commit + tag.

    git push --follow-tags

Reference material

.reference/ contains committed-but-unpublished source from the upstream Figma export tool we reverse-engineered. Read the files there (especially the chunk-*.pretty.js extracts) when you need to understand a behaviour the canonical exporter produces. See .reference/README.md.