【Adobe XD × Zeplin】ハンドオフの地獄を断つ:デザイン・エンジニアリングの極限同期パイプライン
デザインデータのエクスポート、Slackでの「フォントサイズいくつですか?」という不毛なメンション、修正のたびに乖離していく仕様書。これらはすべて、UI/UXデザインと実装のパイプラインにおける「構造的な欠陥」の現れに他ならない。
プロダクトの規模が拡大し、エンジニアリング組織がスケールするほど、デザイナーとエンジニアのハンドオフ(引き継ぎ)コストは指数関数的に増大する。Adobe XDの強力なプロトタイピング能力と、Zeplinの洗練されたアセット・仕様管理レイヤー。この2つを単に「連携させる」だけでは不十分だ。
本稿では、API、CLI、そしてデザイントークンの概念を極限まで突き詰め、人間の手による介入を排除した完全自動化ハンドオフ・パイプラインの構築手法を、骨の髄まで解説する。
—
1. アーキテクチャの全容:なぜ「手動ハンドオフ」は破綻するのか
伝統的なワークフローでは、デザイナーがXD上でアートボードを整理し、手動でZeplinにパブリッシュ、エンジニアがそれを目視で確認しながらCSSに起こすというフローをとる。しかし、このプロセスには致命的なボトルネックがある。
- シングル・オブ・トゥルース(真実の情報源)の崩壊: 画面上のデザインとZeplin上の数値、そしてコードベース(React/Vueなど)のスタイルの間にタイムラグと解釈のズレが生じる。
- デザイントークンの不在: カラーコードやスペーシングが「ハードコード」された状態で引き渡されるため、デザシステムとの統合時に破綻する。
これを解決するためには、「XDの変更を検知し、Zeplinを経て、リポジトリのデザイントークンまでをCI/CDパイプラインの一部として同期する」というエンジニアリングアプローチが必要となる。
[ Adobe XD ] –(XD API / Plugin)–> [ Zeplin Workspace ]
│
(Zeplin CLI / Webhook)
▼
[ GitHub Actions (CI/CD) ]
│
▼
[ デザイントークン自動生成 (JSON/CSS) ]
—
2. Zeplin CLIを用いたアセット・トークンの自動同期ハック
Zeplinを単なる「デザインビューア」として扱うのは、そのポテンシャルの20%しか引き出せていない。ZeplinのCLIとAPIを活用することで、ビルドプロセスやデプロイメントパイプラインにデザインデータを組み込むことが可能になる。
設定ファイル (`zeplin.json`) の極限最適化
プロジェクトのルートに配置する `zeplin.json` を適切に構成し、不要なアセットの書き出しコストを削減する。
{
“projects”: [
{
“id”: “YOUR_ZEPLIN_PROJECT_ID”,
“platform”: “web”,
“colors”: [
{
“name”: “primary/brand”,
“value”: { “r”: 0, “g”: 102, “b”: 204, “a”: 1 }
},
{
“name”: “surface/background”,
“value”: { “r”: 248, “g”: 249, “b”: 250, “a”: 1 }
}
],
“spacing”: [4, 8, 16, 24, 32, 64]
}
]
}
この設定により、Zeplin上で定義されたトークンがエンジニア側のデザインシステム(Tailwind CSSやStyled Componentsなど)と厳密に型安全に同期される基盤が整う。
—
3. GitHub Actions × Zeplin API による「完全自動ハンドオフ」パイプライン
デザイナーがXDからZeplinへパブリッシュした瞬間、あるいは特定のマイルストーンに達した際、エンジニアが手動でコードを確認しに行く必要すらない状態を作る。Zeplin WebhookとGitHub Actionsを結合し、デザイントークンの自動コード生成を行うパイプラインを構築する。
以下のNode.jsスクリプトは、Zeplin APIから最新のカラーパレットとスタイル情報を取得し、CSS変数の定義ファイル (`_tokens.css`) を自動生成するCI用スクリプトの核心部である。
/
- @file generate-tokens.js
- @description Zeplin APIからデザイントークンをフェッチし、CSS Custom Propertiesを生成するスクリプト
/
const https = require(‘https’);
const fs = require(‘fs’);
const path = require(‘path’);
const ZEPLIN_API_TOKEN = process.env.ZEPLIN_API_TOKEN;
const PROJECT_ID = process.env.ZEPLIN_PROJECT_ID;
const options = {
hostname: ‘api.zeplin.dev’,
path: `/v1/projects/${PROJECT_ID}/colors`,
method: ‘GET’,
headers: {
‘Authorization’: `Bearer ${ZEPLIN_API_TOKEN}`,
‘Accept’: ‘application/json’
}
};
const req = https.request(options, (res) => {
let data = ”;
res.on(‘data’, (chunk) => {
data += chunk;
});
res.on(‘end’, () => {
if (res.statusCode !== 200) {
console.error(`Error: Failed to fetch tokens. Status: ${res.statusCode}`);
process.exit(1);
}
const response = JSON.parse(data);
const colors = response.colors || [];
// CSS Custom Propertiesの生成
let cssOutput = ‘/ Auto-generated by Zeplin CI Pipeline. DO NOT EDIT. /\n:root {\n’;
colors.forEach(color => {
// デザイントークンの命名規則(slashをhyphenに変換)
const tokenName = color.name.replace(/\//g, ‘-‘);
const rgba = `rgba(${color.value.r}, ${color.value.g}, ${color.value.b}, ${color.value.a})`;
cssOutput += ` –${tokenName}: ${rgba};\n`;
});
cssOutput += ‘}\n’;
const outputPath = path.join(__dirname, ‘../src/styles/_tokens.css’);
fs.writeFileSync(outputPath, cssOutput, ‘utf8’);
console.log(`Successfully generated design tokens at: ${outputPath}`);
});
});
req.on(‘error’, (error) => {
console.error(`Network Error: ${error.message}`);
process.exit(1);
});
req.end();
GitHub Actions Workflow 定義 (`.github/workflows/sync-tokens.yml`)
name: Sync Design Tokens from Zeplin
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: Run Token Generation Script
env:
ZEPLIN_API_TOKEN: ${{ secrets.ZEPLIN_API_TOKEN }}
ZEPLIN_PROJECT_ID: ${{ secrets.ZEPLIN_PROJECT_ID }}
run: node scripts/generate-tokens.js
- name: Create Pull Request if tokens changed
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: ‘chore(tokens): auto-sync design tokens from Zeplin’
title: ‘🔄 自動同期: Zeplin デザイントークンの更新’
body: ‘Zeplin上のデザイン定義に基づき、デザイントークンを自動更新しました。差分を確認してマージしてください。’
branch: ‘bot/sync-design-tokens’
delete-branch: true
—
4. Adobe XDの大規模運用におけるメモリフットプリントとパフォーマンス最適化
大規模なデザインシステム(数千のアートボード、数百のコンポーネント)をAdobe XDで構築・運用しているチームは、しばしばメモリリークやアプリケーションのフリーズに直面する。Zeplinへスムーズにデータを流すための、XD側のパフォーマンス最適化ハックを共有する。
1. コンポーネントのネスト深度の制限:
ネストが深すぎるシンボル(コンポーネント)は、Zeplin側のDOM構造を肥大化させ、生成されるCSSのセレクタやコード品質を著しく低下させる。ネストは最大3階層までに制限する。
2. ビットマップアセットのSVG/ベクトル化:
不必要なラスター画像の使用は、XD自体のメモリ消費量を跳ね上げ、Zeplinへのエクスポート時間を引き延ばす。アイコンやUIパーツはすべてベクターパスとして定義し、SVGとしてエクスポートされるように整備する。
3. アートボードのチャンク分割(モジュール化):
1つの `.xd` ファイルにプロダクト全体を詰め込むのはアンチパターンである。ドメインごと(例: `auth.xd`, `dashboard.xd`, `settings.xd`)にファイルを分割し、それぞれを個別のZeplinプロジェクト(またはセクション)にマッピングすることで、API同期のペイロードサイズを最小化し、高速なハンドオフを実現する。
—
5. 結び:真のシームレス・ハンドオフへ向けて
ツールを導入しただけでは、組織の壁は取り払えない。Adobe XDとZeplinを高度に結合させ、さらにCI/CDパイプラインと直結させることで初めて、「デザインがそのままコードになる」という理想郷に近づくことができる。
エンジニアとデザイナーが「仕様の解釈」という無駄なコストから解放され、本質的なプロダクトの価値創造に集中するための基盤――それこそが、この極限自動化ワークフローの真の価値である。