Figmaプラグイン自作入門:TypeScriptで実現する、チームの生産性を限界突破させる社内ニッチ自動化ツールの極意
こんにちは。テックリードとして日々デザインシステムと向き合っていると、必ず直面する壁がある。それは「既製のFigmaプラグインでは、うちのチームの特殊な要件を100%満たせない」というもどかしさだ。
デザインの命名規則、特定コンポーネントの構造バリデーション、ローカライズに伴うテキストの変形や一括置換——。これらをヒューマンエラーに頼って手動でチェックしているようでは、エンジニアリング組織としての誇りがすたる。
今回は、既存のツールに骨を折るのをやめ、TypeScriptを用いて「社内ニッチな自動化プラグイン」を自作し、チームの開発スピードを劇的に高める方法を、プロの実践テクニックを交えて徹底解説する。
—
1. 開発スピードを極限まで高める:Figmaショートカット & 神プラグイン
自作プラグインを開発するフェーズにおいても、日常のUI/UX作業においても、キーボードから手を離さないことがスピードの絶対条件だ。
隠れたキーボードショートカット(macOS)
- `⌥ + ⌘ + P`:前回実行したプラグインを再実行(プラグイン開発のデバッグで指が覚えるレベルで使う)
- `⇧ + ⌘ + I`:インスペクト(開発モード)のトグル切り替え
- `⌘ + ⌥ + G`:コンポーネントをフレームにラップ(自動化の起点を作る時によく使う)
- `⌃ + G`:レイアウトグリッドの表示/非表示(視覚的ノイズを瞬時に消す)
開発者・デザイナー必携の「神プラグイン」3選
プラグインを自作する前に、エコシステムのベストプラクティスを知っておく必要がある。
1. Token Studio for Figma (旧Figma Tokens):デザインシステムとコードのSingle Source of Truth(信頼できる唯一の情報源)を構築するための必須解。
2. Linter:レイアウトの崩れやスタイルガイド違反をリアルタイムで検知。自作プラグインのアイデアの源泉になる。
3. Component Code Generator:Figmaのノード構造がどのようにコードに変換されるかを学ぶためのリバースエンジニアリングツール。
—
2. 開発環境の構築:TypeScriptで堅牢なプラグインを作る
Figmaプラグインは、UIスレッド(HTML/CSS/JS)とプラグインスレッド(Node.jsベースのサンドボックス環境、TypeScript対応)の2つのレイヤーで動作する。このアーキテクチャを理解することが成功の鍵だ。
プロジェクトの初期化
公式のボイラープレートを使用するのが最も堅実だ。
npx create-figma-plugin –template=default-typescript my-figma-plugin
cd my-figma-plugin
npm install
ディレクトリ構造は以下のように整理される。
my-figma-plugin/
├── package.json
├── tsconfig.json
├── manifest.json # Figmaプラグインのメタデータ
├── src/
│ ├── code.ts # プラグインロジック(Figma API操作)
│ ├── ui.tsx # ユーザーインターフェース(React / Preact等)
│ └── types.ts # メッセージング用の型定義
—
3. 実践:チームの規律を守る「命名規則チェッカー&一括置換」プラグイン
今回は、実務で最も需要が高い「特定の命名規則(BEM風や特定プレフィックス)に違反しているレイヤーを検知し、ワンクリックで修正・一括置換するプラグイン」のコアロジックを実装する。
設定ファイル(JSON)によるルール定義の外部化
チームの規律はコードにハードコーディングせず、設定ファイルとして分離すべきだ。
// rules.json (プラグイン内に同梱、または将来的にリモートから取得)
{
“prefixRules”: {
“component”: “comp/”,
“layout”: “l-“,
“element”: “el-”
},
“forbiddenWords”: [“Rectangle”, “Frame 」「”, “Group “]
}
1. プラグインのメタデータ定義 (`manifest.json`)
{
“name”: “Design System Guardian”,
“id”: “1234567890abcdef”,
“api”: “1.0.0”,
“main”: “lib/code.js”,
“ui”: “lib/ui.html”,
“editorType”: [“figma”],
“networkAccess”: {
“allowedDomains”: [“none”]
}
}
2. バックエンドロジック (`src/code.ts`)
Figmaのドキュメントツリーを再帰的に走査し、バリデーションと置換を行うプロフェッショナルなコードだ。
// src/code.ts
import rules from ‘./rules.json’;
figma.showUI(__html__, { width: 320, height: 400 });
interface LintError {
id: string;
name: string;
message: string;
}
// ノードを再帰的に走査してバリデーションを行う関数
function validateNode(node: SceneNode, errors: LintError[] = []): LintError[] {
// 禁止ワードのチェック
for (const word of rules.forbiddenWords) {
if (node.name.includes(word)) {
errors.push({
id: node.id,
name: node.name,
message: `デフォルトの命名規則”${word}”が含まれています。`,
});
break;
}
}
// 子要素を持つノードの再帰処理
if (‘children’ in node) {
for (const child of node.children) {
validateNode(child, errors);
}
}
return errors;
}
// UIからのメッセージを受け取るハンドラー
figma.ui.onmessage = async (msg) => {
if (msg.type === ‘run-lint’) {
const selection = figma.currentPage.selection;
const targetNodes = selection.length > 0 ? selection : figma.currentPage.children;
let allErrors: LintError[] = [];
for (const node of targetNodes) {
validateNode(node, allErrors);
}
// UIスレッドへ結果を送信
figma.ui.postMessage({ type: ‘lint-results’, errors: allErrors });
}
if (msg.type === ‘fix-names’) {
// 例:禁止ワードをクリアなプレフィックスに置換
const selection = figma.currentPage.selection;
const recursiveFix = (node: SceneNode) => {
let newName = node.name;
rules.forbiddenWords.forEach(word => {
newName = newName.replace(new RegExp(word, ‘g’), ‘el-‘);
});
node.name = newName;
if (‘children’ in node) {
node.children.forEach(recursiveFix);
}
};
selection.forEach(recursiveFix);
figma.notify(‘✨ 命名規則の自動修正が完了しました!’);
}
};
3. フロントエンドUI (`src/ui.tsx`)
Reactを用いたミニマルで洗練されたコントロールパネルの構築。
// src/ui.tsx
import React, { useEffect, useState } from ‘react’;
import as ReactDOM from ‘react-dom/client’;
import ‘./ui.css’;
interface ErrorItem {
id: string;
name: string;
message: string;
}
function App() {
const [errors, setErrors] = useState
useEffect(() => {
window.onmessage = (event) => {
const { type, errors } = event.data.pluginMessage;
if (type === ‘lint-results’) {
setErrors(errors);
}
};
}, []);
const runLint = () => {
parent.postMessage({ pluginMessage: { type: ‘run-lint’ } }, ”);
};
const fixNames = () => {
parent.postMessage({ pluginMessage: { type: ‘fix-names’ } }, ”);
setErrors([]); // リセット
};
return (
Design Guardian
{errors.length > 0 && (
)}
{err.message}
))}
{errors.length === 0 && (
エラーはありません。素晴らしい状態です!
)}
);
}
const container = document.getElementById(‘root’);
if (container) {
const root = ReactDOM.createRoot(container);
root.render(
}
—
4. チーム開発における設定共有と配布のベストプラクティス
作成したプラグインをチーム全体に展開し、常に同期させるためのオペレーション設計がテックリードの腕の見せ所だ。
1. マニフェストのローカル開発パス登録
プラグインをFigmaデスクトップアプリに読み込めるようにする。
1. Figmaのメニューから Plugins > Development > Import plugin from manifest… を選択。
2. 作成したプロジェクト内の `manifest.json` を指定。
2. GitHubリポジトリでのソースコード管理とCI
プラグインのソースコードは、デザインシステムのリポジトリ、あるいは専用のモノレポ(例: Turborepo等)の `packages/figma-plugins/` 配下で管理する。
.github/workflows/plugin-ci.yml
name: Figma Plugin CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run build
# 必要に応じて社内配布サーバーへのアップロードやSlack通知を組み込む
3. 「Manifestの定数化」による環境差異の吸収
もし将来的にプライベートプラグインとしてFigma Organization内で共有する場合、`manifest.json` の `id` フィールドが各開発者で競合しないよう、CI環境やドキュメントで明確なガイドラインを引いておくこと。
—
5. おわりに:ツールに縛られるな、ツールを創れ
優れたプロダクトデザインとフロントエンド実装の境界線は、自動化ツールの成熟度によって限りなくゼロに近づく。既存の仕様やプラグインの制約にイライラさせられる時間は今日で終わりにしよう。
TypeScriptによる堅牢な型安全性と、Figma APIが提供する強力なノード操作のコンビネーションがあれば、あなたのチームのワークフローを最適化するカスタムツールは、数時間の手間で手に入る。
さあ、エディタを開き、チームの生産性を限界突破させるコードを書き始めよう。