Skip to main content
Glama
ChenYCL

web-design-harvester

by ChenYCL

web-design-harvester

レンダリング済みのWebページを、LLMが構築に使えるデザイン仕様に変換します。セクションごとのスクリーンショット、抽出・凝縮されたcomputed CSS、デザイントークン、レスポンシブ差分、そしてアルファチャンネルまで分析されたアセットを出力します。

Figma Sitesのページを再現するために作られましたが、Figma固有の機能は何も使っていません。レンダリングされるURLなら何でも動作します。

npm install
node bin/harvest.mjs https://example.figma.site --out ./spec --clean
# then point a model at ./spec/README.md

なぜこれを作ったのか

デザインの再現はかつてこうでした。Figmaでブロックを選択 → Copy all CSS → 2000行以上をチャットに貼り付ける → モデルが重要ないくつかの値を掘り起こす → 不一致のスクリーンショットを撮る → これを7〜8回繰り返す。

この工程のすべてのステップが機械的であり、しかも元の素材は見た目よりも質が悪いのです。FigmaのCSSエクスポートには、読めない負の座標を持つ回転フレーム、display: noneのプレースホルダーレイヤー、デスクトップとモバイルのバリアントが混在した状態が含まれています。

レンダリングされたDOMにはそうした問題は一切ありません。ライブページに対するgetComputedStyle()は、任意のブレークポイントにおける解決済みの真実であり、ネットワークのアセットリストも付いてきます。このツールはそれを読み取り、書き出します。

FigmaのREST APIは選択肢にならない

GET /v1/files/{key}は、editorType: "sites"または"make"のファイルに対して**400 "File type not supported by this endpoint"**を返します。読み取れるのはクラシックなdesignファイルだけです。抽出を計画する前に/v1/files/{key}/metaを確認してください。/metaと/stylesはすべてのタイプで動作しますが、ノードエンドポイントは動作しません。


Related MCP server: PicassoWeb

ページをブラウザに表示させる

ここでつまずく人が多いので、正確に説明する価値があります。

対象

動作するか

方法

公開サイト https://<name>.figma.site

✅ はい

URLを渡すだけです。通常の公開ページです。

プレビューiframe https://<uuid>-v2-figmaiframepreview.figma.site

❌ いいえ

下記参照。

未公開サイト(自分のChromeで開いている)

✅ はい

--cdp — 下記参照。

その他のサイト、localhost、ステージング

✅ はい

URLを渡すだけです。

プレビューiframeのURLは単体では動作しない

ページのように見えてHTTP 200を返しますが、取得するとpostMessageリスナーのみを含む約3.6KBのシェルが返ってきます。それ自体にはコンテンツがありません。

// what that URL actually serves, in full:
window.addEventListener('message', (e) => {
  if (isAllowedOrigin(e.origin)) {          // only figma.com and friends
    if (e.data.type === 'iframe-init') {
      script.src = e.data.initScriptURL      // ← the real app comes from the parent

サイトのコードは、ログイン済みのfigma.comタブからのMessagePort経由で届きます。URLを直接読み込むと、どれだけ待っても空白のドキュメントが返るだけです。提供すべきトークンも、設定すべきヘッダーもありません。コンテンツがそもそも存在しないのです。

未公開サイトの場合:自分のブラウザにアタッチする

リモートデバッグ付きでChromeを起動し、Figmaにログインしてサイトのプレビューを開き、ハーベスターをそのタブに向けます。

# 1. Chrome with a debugging port (use a separate profile to avoid clobbering yours)
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 --user-data-dir=/tmp/figma-profile

# 2. Log into figma.com in that window, open your Sites file, hit Preview.

# 3. Harvest the rendered iframe
node bin/harvest.mjs "https://<uuid>-v2-figmaiframepreview.figma.site" \
  --cdp 9222 --out ./spec

--cdpを使うと、ツールはあなたのブラウザにアタッチし、何も起動・終了しません。より簡単な代替案(可能であれば)は、サイトを公開して公開URLを収穫することです。

毎回ログインをやり直したくないログイン必須のサイトには、--persistでプロファイルをディスクに保持できます。


使い方

harvest <url> [options]              full harvest -> spec directory
harvest outline <url>                print the DOM outline (recon)
harvest blocks <url>                 list blocks that would be captured
harvest asset <file...>              analyse local media files
harvest serve [--port 8787]          HTTP daemon, browser stays warm
harvest mcp                          MCP server on stdio

オプション

デフォルト

説明

--out <dir>

./out

出力ディレクトリ

--widths <list>

1440,375

ブレークポイント(例: 1440,768,375)

--selector <css>

auto

ブロック境界を強制

--settle <ms>

800

ページ安定後の追加待機時間

--max-nodes <n>

400

ブロックあたりのノード上限

--skip-assets

アセットのダウンロードと分析をスキップ

--clean

出力ディレクトリを先に消去

--headed

ブラウザウィンドウを表示

--persist [dir]

プロファイルを再利用しログインを維持

--cdp <endpoint>

実行中のChromeにアタッチ(ポートまたはws://URL)

--json

機械可読なstdout

見慣れないページではoutlineまたはblocksから始めてください。 これらは高速で、フル実行に踏み切る前に自動セグメンテーションが妥当なセクションを見つけられるかを教えてくれます。

node bin/harvest.mjs blocks https://figma.site --widths 1440
strategy: semantic-landmarks
coverage: 100% (11248 of 11248px)

  01  header.fig-suku18       1440×78 @0  [sticky]  What you can do in figma
  02  section.fig-lqoz33    1440×1109 @78            Figma Sites
  03  section.fig-15ba1hq   1440×1117 @1187          Perfect websites every time…
  …

境界が正しくない場合は、--selector "main > section"を渡してください。


出力

spec/
  README.md          ← start here; index, warnings, token summary
  index.json         machine-readable manifest
  tokens.md          design tokens ranked by usage
  tokens.css         the same tokens as CSS custom properties
  responsive.md      every value that changes between breakpoints
  interactions.md    clickable/focusable elements and their transitions
  warnings.json      asset fit problems, in full
  page-desktop.png   full-page screenshot per breakpoint
  page-mobile.png
  blocks/
    02-figma-sites/
      block.md       ← spec sheet for one section
      desktop.png    screenshot, exactly the block's size
      mobile.png
      tree.desktop.json   exact computed values, full precision
      tree.mobile.json
  assets/
    README.md        every asset with content box and fit guidance
    manifest.json
    <files>          deduplicated by content hash

block.mdは次のようになります。

section.fig-lqoz33            1440×1108.6  pad:0/0/32/0  relative  bg:#ffffff
└─ div.fig-umtrpl             1440×1076.6  flex-col  gap:64  pad:64/0/0/0
   ├─ h1.fig-6late5              660×72     mar:0/0/32/0  72/72  ls:-1.44  "Figma Sites"
   └─ a.fig-1jz30fp            135×46.4     flex-row  jc:center  pad:12/22
                                            #ffffff  bg:#000000  r:8  href:/site/new

さらに、ブレークポイントごとのジオメトリ、コピー、使用されたアセット、レスポンシブ差分テーブルが含まれます。隣接するJSONには、何かおかしい場合に備えて完全版の値が入っています。


スクリーンショットだけではできないこと

スクリーンショットは1 CSSピクセル = 1画像ピクセルです。 deviceScaleFactor: 1とscale: 'css'を組み合わせることで、PNGから測定した距離がそのままCSSピクセルになります。換算係数がないので、換算ミスもありません。(Figmaの@2xエクスポートでは、1440pxデザインに対して1798画像ピクセルが生成されるため、すべての測定値をまず1.2486で割る必要があり、これを間違えるともっともらしいが誤った数値が生まれました。)

Computedスタイルはダンプではなく蒸留されます。 すべての要素に対して3つのフィルターが実行されます。そのタグのUAデフォルトは除外され、親がすでに宣言している継承値は除外され、ほぼすべてのノードに現れる宣言(box-sizing: border-boxなど)は切り出されて一度だけ記述されます。残るのは差分だけであり、それが実際に書くべきものです。実際には、生のダンプよりおよそ1桁小さくなります。

アセットは推測ではなく測定されます。 すべての画像と動画について、ツールはフレームをデコードし、アルファチャンネルからコンテンツのバウンディングボックスを特定します。デザインアセットはしばしば1200×1200の正方形で、アートワークが中心からずれた1049×677の領域に収まっていることがあります。ファイルの寸法だけではこれは見えず、object-contain(余白ができる)もobject-cover+中央寄せ(軸がずれて切り取られる)も正しくありません。出力には使用すべきobject-positionが明記されます。

アルファ付きVP9 WebMは特別に処理されます。ffprobeはpix_fmt=yuv420pを報告しアルファチャンネルを表示しませんが、ブラウザは正しく合成します。アルファはlibvpx-vp9デコーダーを強制した場合にのみ現れます。

不可能なレイアウトは初回実行で指摘されます。 アセットのコンテンツ比率とそれが置かれているボックスが大きく食い違う場合、どのobject-fit値でも修正できません。アセットの再エクスポートが必要です。READMEはそれを即座にフラグし、coverが破棄するアートワークの割合も示します。CSSを調整してエクスポートの問題を修正しようと6ラウンドも費やすことはありません。

両方のブレークポイント。仕様の半分は差分にあるからです。 カードの角丸12px → 6px、タイトル20/30 → 16/24、ヘッダーのパディング32px → 32px(変更なし)。これらはどれもスケーリングでは導出できません。responsive.mdは変化するすべての値を列挙します。

任意の数のブレークポイント。 --widths 1440,768,375で3つ取得でき、すべてがそれに応じて拡張されます。ブレークポイントごとのスクリーンショットとスタイルツリー、tokens.mdでのブレークポイント別の使用回数、そして各隣接ペアの差分テーブル — desktop → tablet、次にtablet → mobileです。すべてをデスクトップと比較するのではなく隣接比較にするのは、メディアクエリの書き方を反映しているからです。各ステップは前のステップから変更された点だけを言い直します。responsive.mdは、どのブロックがどのステップで変化するかのマトリックスで始まります。

何も黙って破棄されません。 セグメンテーション後、ツールはブロックがページを隙間なく覆っているかを確認し、隙間を覆う要素を探し、カバレッジ比率を報告します。本当に取得できない領域は無視するのではなく列挙されます。スクリーンショットの寸法は要求されたものと照合されます。切り詰められたキャプチャは失敗したものより悪いからです。見た目は正常で、そこから測定したすべての値が静かに間違っているからです。


モデルへの提供

コールドランはほとんどの時間をブラウザ起動と初回ペイントに費やします。モデルが反復している場合 — あのブロックをもう一度、今度は768pxで、あのアイコンの色は何色か — 質問ごとにそのコストを払うとツールは使い物になりません。両方のサーバーモードはブラウザと読み込み済みページを温かい状態に保ちます。

https://figma.siteでの測定値: コールド26.8秒 → ウォーム0.03秒。

MCP(stdio)

{
  "mcpServers": {
    "web-design-harvester": {
      "command": "node",
      "args": ["/absolute/path/to/web-design-harvester/bin/harvest.mjs", "mcp"]
    }
  }
}

ツール: harvest_outline、harvest_blocks、harvest_block、harvest_tokens、harvest_assets、harvest_screenshot、harvest_analyse_asset、harvest_site、harvest_status。

典型的なループは、harvest_blocksでセクションを見つけ、次にharvest_blockでその構造を取得するというものです。2回目の呼び出しはページがすでに開いているため、数十ミリ秒で完了します。

HTTPデーモン

node bin/harvest.mjs serve --port 8787
curl "http://127.0.0.1:8787/blocks?url=https://figma.site&width=1440"
curl "http://127.0.0.1:8787/block?url=https://figma.site&index=4"

ループバックのみにバインドします。任意のURLを取得して指定された場所にファイルを書き込むため、外部から到達可能であってはなりません。本当に必要な場合のみ--hostを渡してください。アイドル状態のページは10分後に閉じられます。


要件

  • Node 18以上

  • Playwright Chromium — npm installがpostinstallフック経由で取得します

  • ffmpeg / ffprobe (任意) — コンテンツボックス、パレット、動画分析に必要です。これがなくても他の機能はすべて動作します。アセットのインテリジェンスは警告付きでスキップされます。brew install ffmpeg

npm test    # 46 tests, ~10s, hermetic (local fixture, no network)

既知の制限

  • クロスオリジンiframeはDOMとスクリーンショットの両方で穴になります。ツールはそれらを検出し、サイズ・オリジン・srcをblock.mdに列挙しますが、内部は見えません。フレームがレンダリングするものは別途処理する必要があります。

  • ホバーとフォーカスのスタイルは取得されません。ライブな操作が必要だからです。interactions.mdは各要素のtransitionプロパティと時間を示し、何がどの速さでアニメーションするかはわかりますが、最終状態はわかりません。

  • ストリーミング動画(DASH/fMP4)は単体では有効でないセグメントとして届きます。これらは破損として報告するのではなく、そのようにラベル付けされます。

  • CanvasとWebGLのコンテンツはスクリーンショット内のピクセルとして取得されます。抽出できる構造はありません。

  • スクロール駆動アニメーションは1時点でサンプリングされます。ページはまず端から端までスクロールされて出現をトリガーし、その後先頭に戻ります。スクロール位置に依存して出現するセクションは、最終状態にない可能性があります。

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Analyzes and extracts design tokens, assets, and layout from live websites to enable AI clients to faithfully replicate them, with tools for screenshotting, component inspection, and pixel-diff verification.
    14
    23 npm
    4
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to inspect web pages deterministically as token-cheap visual geometry instead of pixel dumps, surfacing layout tooling for overlaps, overflow, WCAG contrast ratios, and quality scoring. Agents can pass HTML source or a live URL per call and receive byte-identical, actionable findings they can act on and re-verify.
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Extract design tokens, colors, typography, spacing and components from any live website, and compute design drift against an approved baseline. Exports DESIGN.md, DTCG, Tailwind and shadcn.
    5,798 npm
    3,878
    MIT