🙆‍♀️

Figmaのコンポーネントにメタ情報を持たせた上でFigma MCPを使ったらUI再現率が格段に上がった話

に公開

前回の記事の続きです。

前回、MUIのfigmaコンポーネントを用いてfigmaを作成し、そのfigmaをfigmaMCPを通じてcursorに与えてあげたら、正しいコンポーネント、propsを使ってUI再現できるのではないかと実験しました。

概ねいい感じですが、完璧ではありませんでした。
得られた結果の考察としては以下のようになっています。

良い点

  • 使うべきコンポーネントの特定は正しく行えた。

悪い点

  • 渡すべきpropsを正しく渡せていない。

上記を解決するための手段はいくつかあると思います。その中の一つにはデザインシステムの情報を返すmcpを作って繋げてあげるというものもあるでしょう。
しかし、おそらくもっとシンプルにこの問題は解決できると思います。

それは、figma mcpからcursorにinjectされる生データを見ればわかります。

左の画像のselectをmcpでcursorに読み込ませます。

image.png

得られる生データは以下のようになっています。

<details>
<summary>JSONデータを表示 (クリックして展開)</summary>

{
  "metadata": {
    "name": "Untitled",
    "lastModified": "2025-04-11T09:09:40Z",
    "thumbnailUrl": "https://s3-alpha.figma.com/thumbnails/f3f20862-1e24-4f0e-bfcb-051890c1b09a?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAQ4GOSFWCTQQOBFQB%2F20250410%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20250410T000000Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=cb4a360d80bbf4054b65e46c0caf661077b8f1cb2321b34a06b0aa773be9ae41"
  },
  "nodes": [
    {
      "id": "2:965",
      "name": "<TextField>",
      "type": "INSTANCE",
      "fills": "fill_WISCV3",
      "layout": "layout_MBBBPH",
      "children": [
        {
          "id": "I2:965;6594:982891",
          "name": "Input",
          "type": "FRAME",
          "strokes": "stroke_V2OBQR",
          "layout": "layout_CRAFLB",
          "borderRadius": "4px",
          "children": [
            {
              "id": "I2:965;6594:982892",
              "name": "Content",
              "type": "FRAME",
              "layout": "layout_MOT75M",
              "children": [
                {
                  "id": "I2:965;6594:982895",
                  "name": "Value",
                  "type": "TEXT",
                  "textStyle": "style_5W7R49",
                  "fills": "fill_VA41NS",
                  "layout": "layout_WOJRNI"
                }
              ]
            },
            {
              "id": "I2:965;6594:982902",
              "name": "Label Container",
              "type": "FRAME",
              "fills": "fill_WISCV3",
              "layout": "layout_DXY51F",
              "children": [
                {
                  "id": "I2:965;6594:982903",
                  "name": "Label",
                  "type": "TEXT",
                  "textStyle": "style_0IOED9",
                  "fills": "fill_Q28O44",
                  "layout": "layout_HIPXLS",
                  "text": "姓"
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "globalVars": {
    "styles": {
      "fill_WISCV3": ["#FFFFFF"],
      "layout_MBBBPH": {
        "mode": "column",
        "sizing": {
          "horizontal": "fixed",
          "vertical": "hug"
        }
      },
      "stroke_V2OBQR": {
        "colors": ["rgba(0, 0, 0, 0.23)"],
        "strokeWeight": "1px"
      },
      "layout_CRAFLB": {
        "mode": "column",
        "alignSelf": "stretch",
        "padding": "0px 12px",
        "sizing": {
          "horizontal": "fill",
          "vertical": "hug"
        }
      },
      "layout_MOT75M": {
        "mode": "row",
        "alignItems": "center",
        "alignSelf": "stretch",
        "padding": "8px 0px",
        "sizing": {
          "horizontal": "fill",
          "vertical": "hug"
        }
      },
      "style_5W7R49": {
        "fontFamily": "Roboto",
        "fontWeight": 400,
        "fontSize": 16,
        "lineHeight": "1.5em",
        "letterSpacing": "0.937500037252903%",
        "textAlignHorizontal": "LEFT",
        "textAlignVertical": "TOP"
      },
      "fill_VA41NS": ["rgba(0, 0, 0, 0.87)"],
      "layout_WOJRNI": {
        "mode": "none",
        "sizing": {
          "horizontal": "fill",
          "vertical": "hug"
        }
      },
      "layout_DXY51F": {
        "mode": "row",
        "alignItems": "center",
        "gap": "10px",
        "padding": "0px 4px",
        "sizing": {
          "horizontal": "hug",
          "vertical": "fixed"
        },
        "position": "absolute",
        "locationRelativeToParent": {
          "x": 12,
          "y": 0
        }
      },
      "style_0IOED9": {
        "fontFamily": "Roboto",
        "fontWeight": 400,
        "fontSize": 12,
        "lineHeight": "1em",
        "letterSpacing": "1.2500000496705375%",
        "textAlignHorizontal": "LEFT",
        "textAlignVertical": "CENTER"
      },
      "fill_Q28O44": ["rgba(0, 0, 0, 0.6)"],
      "layout_HIPXLS": {
        "mode": "none",
        "sizing": {
          "horizontal": "hug",
          "vertical": "hug"
        }
      }
    }
  }
}

selectを構成している要素たちの階層構造や名前、スタイルがjson形式でまとまっています。

しかし、figmaのコンポーネントではselectの見た目をpropertiesで変更できるようになっていますが、現在選択している変数の情報は失われています。

image.png

右側に変数が置いてある。

MUIの公式のコンポーネントを使ってデザインを作る際、この部分を変更します。そして、これは実際にコンポーネントに渡すpropsに対応しています。

そこで、雑ではありますが、以下のように現在選択中のpropsの情報を不透明度0、大きさ0のtextノードで記述するようにしました。
これはつまり、Figma MCPにより注入されるjsonに直接propsの情報まで渡されるようにするためです。

image.png

以下のようにコンポーネントとpropsの情報が含まれる。(実際には透明度と大きさを0にしている)
image.png

実験

image.png

結果

figmaのデザイン 生のMUI公式コンポーネントを使った場合 MUI公式コンポーネントにメタ情報用のテキストノードを追加した場合
image.png image.png image.png

改善するとは思っていましたが、かなり強力にUIが再現できるようになりましたね。
一発でここまで再現してくれるとかなり開発体験がいいです。
特に、sizeやwidthFullなどが適切に当たっているのが適切にメタ情報の効果が感じられました。ついでにiconとかも正しいものが使えてる。

まとめ

figmaのコンポーネントを作る際、ただ単にバリエーションを作る時にメタ情報を置いておくとUIの再現率が格段に上がりました。
今回は使用するコンポーネントのみにメタ情報の付与を行いましたが、MUIの公式のfigma全てのコンポーネントに同様のメタ情報用のノードを追加してあげて、それを使ったデザイン作成を行えば、おそらくかなりUIの微調整等に使う時間はなくなりそうです。(誰かやってくれ~~)
figmaのコンポーネントであれば、一度作ったらあとは使うだけですしやってもいいかも。また、デザインシステムの知識を伝えるMCPとは異なり追加のコンテキストも大きくないので、コンテキストの肥大化も防げるのではないでしょうか。

Discussion