【テクニカル・上級編】Figmaの「Dev Mode」におけるカスタムAnnotation(注釈)機能の活用法!エンジニアとのコミュニケーションを極限まで円滑にする方法 – UI/UX・デザインツール活用バイブル

Figma Dev Mode カスタムAnnotationの極限活用:デザインハンドオフの「非同期破壊」とCI/CDパイプライン統合

幾度となく「デザインと実装の乖離」という名の技術的負債に直面してきたエンジニアたちへ告ぐ。

PDFの仕様書、スプレッドシートの要件定義、チャットツールの流れるログ。これらはすべて、モダンなプロダクト開発において「死んだ資産」である。ソースコードがリポジトリで厳密に管理され、CI/CDによって検証されている現代において、デザインの意図伝達だけが前近代的な手動同期に依存している現状は、アーキテクトとして見過ごすわけにはいかない。

Figmaの「Dev Mode」およびそのカスタムAnnotation(注釈)機能は、単なるメモ書きのツールではない。これは、デザインメタデータをコードベースの近傍まで引き上げ、ハンドオフのオーバーヘッドをゼロにするための強力なプロトコルである。

本稿では、Figma REST API、Webhook、そしてカスタムプラグイン開発を組み合わせ、Dev ModeのAnnotationをただの付箋から「機械可読な仕様書(Machine-Readable Specification)」へと昇華させる、極限の自動化アーキテクチャを解説する。

—

1. デザインハンドオフのパラダイムシフト:仕様書の完全コード化

従来のハンドオフフローは、認知的負荷が高く、非効率極まりない。

  • 旧来のフロー: デザイナーがFigmaで画面を作る $\rightarrow$ 別途仕様書を書く $\rightarrow$ 変更があれば仕様書を更新する(ここで高確率で同期ズレが起きる) $\rightarrow$ エンジニアが質問する。
  • あるべきフロー: Figma上のコンポーネント自体に構造化されたメタデータ(Annotation)を付与し、それが直接CI/CDやチケット管理システムに同期される。

Dev ModeのカスタムAnnotationを活用することで、デザイナーは「実装のためのコンテキスト」をキャンバス上に直接、しかも構造化データとして埋め込めるようになる。

Annotationの本質は「型付きメタデータ」である

Annotationを単なるテキストボックスとして扱っているうちは三流だ。これをJSONスキーマに準拠した型付きのメタデータとして捉える必要がある。

例えば、あるボタンコンポーネントに対して以下の情報をAnnotationとして付与する。

  • State Matrix: 通常、ホバー、フォーカス、無効状態の振る舞い
  • A11y (アクセシビリティ): ARIA属性、キーボードナビゲーションの順序
  • API Contract: 関連するエンドポイントとペイロードの型定義

これらがFigmaのレイヤーノードIDと厳密に紐づくことで、エンジニアは迷うことなくコードへ変換できる。

—

2. 開発者が一目で理解できるAnnotationの構造化ルール

開発者が求めるのは「文学的なデザイン意図」ではなく「厳密な仕様」である。Annotationを運用する際は、以下のタクソノミー(分類規則)をチーム全体で強制しなければならない。

カテゴリ分けのプレフィックス標準

Annotationの先頭には、必ず以下のプレフィックスを付与し、視覚的・プログラム的なパースを容易にする。

| プレフィックス | 対象領域 | 記述内容の例 |
| :— | :— | :— |
| `[API]` | バックエンド連携 | エンドポイント, キャッシュ戦略, エラーハンドリング |
| `[STATE]` | 状態管理 | 状態遷移条件, 初期値, 楽観的UI更新の有無 |
| `[A11y]` | アクセシビリティ | `role`, `aria-expanded`, フォーカストラップ |
| `[PERF]` | パフォーマンス | 遅延ロードの必要性, 画像の最適化フォーマット |

構造化メモのテンプレート(Markdown記法)

FigmaのAnnotationはMarkdownをサポートしている。以下のテンプレートをチームのデザイナーに徹底させろ。

[STATE] ユーザー認証状態による出し分け

  • Condition: `user.isAuthenticated === true`
  • Fallback: 未認証時は `/login` へリダイレクト
  • Animation: Fade-in (duration: 200ms, ease: ease-out)

この構造化により、後述する自動化スクリプトがこのテキストを正規表現でパースし、TypeScriptの型定義やテストケースの骨組みを自動生成することが可能になる。

—

3. 自動化と同期:Figma API × 独自CLIによるパイプライン構築

ここからが本題だ。FigmaのUI上で完結しているAnnotationを、開発のライフサイクルに完全に統合する。Figma REST APIを叩き、Annotationを抽出し、GitHub Actions上でリポジトリの型定義やドキュメントと同期するパイプラインを構築する。

ステップ1: Figma APIからのAnnotation抽出スクリプト

以下のTypeScriptスクリプトは、Figma REST APIを使用して指定されたファイルのノードを走査し、`devStatus` や Annotation(Comments / Section などのメタデータ)を抽出するコアロジックである。

// figma-sync.ts
import axios from ‘axios’;
import as fs from ‘fs’;

const FIGMA_ACCESS_TOKEN = process.env.FIGMA_ACCESS_TOKEN;
const FIGMA_FILE_KEY = process.env.FIGMA_FILE_KEY;

interface AnnotationPayload {
nodeId: string;
nodeName: string;
annotations: string[];
}

async function fetchFigmaAnnotations(): Promise {
const url = `https://api.figma.com/v1/files/${FIGMA_FILE_KEY}`;

try {
const response = await axios.get(url, {
headers: { ‘X-Figma-Token’: FIGMA_ACCESS_TOKEN }
});

const document = response.data.document;
const results: AnnotationPayload[] = [];

// 再帰的にノードを走査し、Annotation(開発用メモ)を抽出する関数
function traverse(node: any) {
if (node.document && node.document.children) {
node.document.children.forEach(traverse);
} else if (node.children) {
node.children.forEach(traverse);
}

// FigmaのDev Modeでアタッチされたプロパティや説明文をキャプチャ
if (node.sharedPluginData && node.sharedPluginData[‘dev-annotations’]) {
results.push({
nodeId: node.id,
nodeName: node.name,
annotations: parsePluginData(node.sharedPluginData[‘dev-annotations’])
});
}

// 標準のdescriptionプロンプトを活用している場合のフォールバック
if (node.description) {
results.push({
nodeId: node.id,
nodeName: node.name,
annotations: [node.description]
});
}
}

traverse(document);
return results;
} catch (error) {
console.error(‘Failed to fetch Figma file data:’, error);
process.exit(1);
}
}

function parsePluginData(data: any): string[] {
// プラグイン固有のバイナリ/JSONデータをデコードする処理
return Object.values(data);
}

// 実行してローカルのJSONとして吐き出す(CI/CDのデータソース)
fetchFigmaAnnotations().then(data => {
fs.writeFileSync(‘./design-tokens/annotations.json’, JSON.stringify(data, null, 2));
console.log(‘Successfully synced Figma annotations to local storage.’);
});

ステップ2: GitHub ActionsによるCI/CD統合

このスクリプトをGitHub Actionsに組み込み、デザインが更新されるたび(あるいは夜間バッチで)自動的にリポジトリ側の仕様ドキュメントを更新、またはコードのモックと突き合わせる。

.github/workflows/sync-annotations.yml
name: Sync Figma Dev Mode Annotations

on:
schedule:

  • cron: ‘0 0 ‘ # 毎日深夜に実行

workflow_dispatch:

jobs:
sync:
runs-on: ubuntu-latest
steps:

  • name: Checkout repository

uses: actions/checkout@v4

  • name: Set up Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’

  • name: Install dependencies

run: npm ci

  • name: Run Figma Annotation Sync

env:
FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }}
FIGMA_FILE_KEY: ${{ secrets.FIGMA_FILE_KEY }}
run: npx ts-node ./scripts/figma-sync.ts

  • name: Create Pull Request if changes exist

uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: ‘chore(design): auto-sync Figma Dev Mode annotations’
title: ‘🔄 Design-Code Sync: Update Figma Annotations’
body: ‘自動化パイプラインにより、Figma Dev Modeの最新Annotationを同期しました。’
branch: ‘chore/figma-sync’

—

4. 認識のズレを防ぐための運用アーキテクチャ(ガバナンス)

ツールと自動化を導入しても、それを扱う人間の運用ルールが崩壊していれば意味がない。エンジニアとデザイナーの間に鉄の掟を敷く。

1. 「Dev Mode Ready」ステータスの義務化

デザイナーは、画面のデザインが完了しただけではプルリクエストを出せない(Figma上でのプルリクエスト的概念)。

  • Figma上のセクションごとに `Dev Mode Status` を “Ready for Dev” に変更する。
  • このステータス変更をトリガーにして、Figma Webhookが検知し、Slackの `#dev-handoff` チャンネルへ自動通知するアーキテクチャを構築する。

2. デザインレビューのコードレビュー化

エンジニアは、デザインレビューをFigma上で行うのではなく、自動生成されたAnnotationの差分(Pull Request)ベースで行う。
「ここの `[STATE]` の定義が曖昧だから、Figma側で修正して再同期してくれ」というやり取りが、そのままGitの履歴として残る。これにより、「言った・言わない」の泥沼劇は完全に消滅する。

—

5. アーキテクトの結論:デザインとコードの境界線を溶かせ

UI/UXデザインとソフトウェアエンジニアリングは、長らく「壁」を挟んで対話してきた。デザイナーはビジュアルの美しさを追求し、エンジニアはその実現可能性と構造に頭を悩ませる。

しかし、FigmaのDev ModeとカスタムAnnotation、そしてAPI駆動の自動化を組み合わせた瞬間、その壁は消え去る。

デザインとは、もはや静的な絵画ではない。「実行可能な仕様書の初期表現(Initial Representation of Executable Specifications)」である。

このパラダイムシフトをいち早く取り入れたチームだけが、圧倒的なスピードと品質でプロダクトを市場に投下し続けることができる。さあ、今すぐ手動のハンドオフを捨て去り、コードとデザインをシームレスに繋ぐパイプラインを構築せよ。

タイトルとURLをコピーしました