figma-design について
figma-design は、Figma のデザインワークフローとプラグイン開発を扱うスキルガイドです。Figma Plugin API の資料に基づき、コンポーネント、オートレイアウト、プロトタイプ、データ管理、アセット出力を解説します。
主な機能
- 01Figma プラグイン開発
- 02コンポーネントとデザインシステムの管理
- 03オートレイアウトと制約
- 04インタラクティブなプロトタイプ設定
- 05ノード検索と一括操作
- 06データ保存とアセット出力
活用シーン
- 01Figma プラグインと自動化ツールの開発
- 02デザインシステムと再利用可能なコンポーネントの構築
- 03インタラクティブなプロトタイプとフローの作成
- 04ノードの一括処理とアセット出力
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ manutej/ luxor-claude-marketplace --skill figma-designインストール後、Claude Code で/figma-designを入力して読み込まれたか確認します。
同梱ファイルEXAMPLES.mdREADME.md
詳細
Figma Design スキル
Context7 の公式 Figma Plugin API ドキュメントに基づいた、Figma のデザインワークフロー、プラグイン開発、コンポーネントシステム、オートレイアウト、プロトタイピング、デザインシステム管理に関する包括的なガイドです。
このスキルを使う場面
以下の作業でこのスキルを使用します。
- Figma プラグイン開発:カスタムプラグイン、UI 拡張機能、自動化ツールの構築
- デザインシステム:変数、スタイル、コンポーネント、ライブラリの作成と管理
- コンポーネントアーキテクチャ:再利用可能なコンポーネント、バリアント、インスタンスの構築
- オートレイアウト:制約と間隔を指定したレスポンシブフレームの実装
- プロトタイピング:リアクションとフローを使ったインタラクティブなプロトタイプの作成
- バッチ操作:複数のノードにまたがる反復的なデザイン作業の自動化
- エクスポートと統合:さまざまな形式(PNG、JPG、SVG)でのアセットのエクスポート
- データ管理:ノード上のプラグインデータや共有データの保存と取得
- コラボレーション:バージョン履歴、コメント、チームのワークフローの管理
基本概念
1. Figma のノード階層
Figma ドキュメントの構造は、ノードのツリーです。
DocumentNode (root)
└── PageNode
├── FrameNode
│ ├── TextNode
│ ├── RectangleNode
│ └── ComponentNode
└── SectionNode
└── FrameNode
主なノードタイプ:
FRAME:オートレイアウト機能を備えたコンテナーCOMPONENT:再利用可能なデザイン要素(マスター)INSTANCE:コンポーネントのコピーTEXT:編集可能なテキストレイヤーRECTANGLE、ELLIPSE、POLYGON、STAR、LINE:基本図形SECTION:フレームを整理するためのコンテナーGROUP:レイアウト機能のないコンテナー
2. コンポーネントとインスタンス
コンポーネントは、複数回インスタンス化できるマスター要素です。
// Create a component
const button = figma.createComponent()
button.name = "Primary Button"
button.resize(120, 40)
// Add visual elements
const bg = figma.createRectangle()
bg.resize(120, 40)
bg.cornerRadius = 8
bg.fills = [{ type: 'SOLID', color: { r: 0.2, g: 0.5, b: 1 } }]
button.appendChild(bg)
// Create instance
const buttonInstance = button.createInstance()
buttonInstance.x = 200
buttonInstance.y = 100
コンポーネントのプロパティ:
BOOLEAN:表示状態や状態を切り替えますTEXT:カスタマイズ可能なテキストコンテンツINSTANCE_SWAP:入れ子になったコンポーネントを置き換えますVARIANT:コンポーネントのさまざまなバリエーション
3. オートレイアウト
オートレイアウトを使うと、コンテンツの変更に合わせて調整されるレスポンシブフレームを作成できます。
主なプロパティ:
layoutMode:'HORIZONTAL'、'VERTICAL'、または 'NONE'primaryAxisSizingMode:'FIXED' または 'AUTO'counterAxisSizingMode:'FIXED' または 'AUTO'paddingLeft、paddingRight、paddingTop、paddingBottomitemSpacing:子要素間の間隔primaryAxisAlignItems:主軸上の配置counterAxisAlignItems:交差軸上の配置
子要素の制約:
minWidth、maxWidth:幅の境界値minHeight、maxHeight:高さの境界値layoutAlign:'MIN'、'CENTER'、'MAX'、'STRETCH'layoutGrow:0(固定)または 1(コンテナーを埋める)
// Create auto-layout frame
const frame = figma.createFrame()
frame.layoutMode = 'VERTICAL'
frame.primaryAxisSizingMode = 'AUTO'
frame.counterAxisSizingMode = 'FIXED'
frame.resize(300, 0) // Width fixed, height auto
frame.itemSpacing = 16
frame.paddingLeft = 24
frame.paddingRight = 24
frame.paddingTop = 24
frame.paddingBottom = 24
// Add children with constraints
const child = figma.createRectangle()
child.resize(252, 100)
child.layoutAlign = 'STRETCH' // Fill width
child.minHeight = 100
child.maxHeight = 200
frame.appendChild(child)
4. 制約
制約は、親のサイズが変わったときにノードがどのようにリサイズされるかを制御します。
interface Constraints {
horizontal: 'MIN' | 'CENTER' | 'MAX' | 'STRETCH' | 'SCALE'
vertical: 'MIN' | 'CENTER' | 'MAX' | 'STRETCH' | 'SCALE'
}
node.constraints = {
horizontal: 'MIN', // Pin to left
vertical: 'MAX' // Pin to bottom
}
制約の種類:
MIN:上端/左端に固定CENTER:親要素の中央に配置MAX:下端/右端に固定STRETCH:親要素に合わせて伸縮(両端)SCALE:位置とサイズの比率を維持
5. 変数とデザイントークン
変数を使うと、動的なデザインシステムを作成できます(Figma Design のみ)。
// Create variable collection
const collection = figma.variables.createVariableCollection('Design Tokens')
// Create color variable
const primaryColor = figma.variables.createVariable(
'color/primary',
collection,
'COLOR'
)
// Set value for default mode
const defaultMode = collection.modes[0]
primaryColor.setValueForMode(defaultMode.modeId, {
r: 0.2, g: 0.5, b: 1, a: 1
})
// Add dark mode
const darkMode = collection.addMode('Dark')
primaryColor.setValueForMode(darkMode, {
r: 0.4, g: 0.7, b: 1, a: 1
})
// Create variable alias (reference)
const accentColor = figma.variables.createVariable(
'color/accent',
collection,
'COLOR'
)
const alias = figma.variables.createVariableAlias(primaryColor)
accentColor.setValueForMode(defaultMode.modeId, alias)
// Bind variable to node
const rect = figma.createRectangle()
const fill = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } } as SolidPaint
const boundFill = figma.variables.setBoundVariableForPaint(
fill,
'color',
primaryColor
)
rect.fills = [boundFill]
変数の種類:
COLOR:RGB/RGBA 値FLOAT:数値(間隔、サイズ)BOOLEAN:真偽値STRING:テキスト値
6. スタイル
スタイルは、再利用可能な視覚プロパティを定義します。
// Paint style (fills/strokes)
const paintStyle = figma.createPaintStyle()
paintStyle.name = 'Brand/Primary'
paintStyle.paints = [{
type: 'SOLID',
color: { r: 0.2, g: 0.5, b: 1 }
}]
// Text style
const textStyle = figma.createTextStyle()
textStyle.name = 'Heading/H1'
textStyle.fontSize = 32
textStyle.fontName = { family: 'Inter', style: 'Bold' }
textStyle.lineHeight = { value: 120, unit: 'PERCENT' }
textStyle.letterSpacing = { value: -0.5, unit: 'PIXELS' }
// Effect style (shadows, blurs)
const effectStyle = figma.createEffectStyle()
effectStyle.name = 'Shadow/Card'
effectStyle.effects = [{
type: 'DROP_SHADOW',
color: { r: 0, g: 0, b: 0, a: 0.15 },
offset: { x: 0, y: 4 },
radius: 8,
visible: true,
blendMode: 'NORMAL'
}]
// Apply styles
rect.fillStyleId = paintStyle.id
text.textStyleId = textStyle.id
frame.effectStyleId = effectStyle.id
プロトタイピング
1. リアクションとインタラクション
リアクションは、プロトタイプにおけるインタラクティブな動作を定義します。
// Set reactions on a node
await node.setReactionsAsync([
{
action: {
type: 'NODE',
destinationId: 'nodeId', // Target frame
navigation: 'NAVIGATE',
transition: {
type: 'SMART_ANIMATE',
easing: { type: 'EASE_IN_AND_OUT' },
duration: 0.3
},
preserveScrollPosition: false
},
trigger: {
type: 'ON_CLICK'
}
}
])
トリガーの種類:
ON_CLICK:クリック/タップON_HOVER:マウスホバーON_PRESS:タッチによる押下ON_DRAG:ドラッグ操作MOUSE_ENTER、MOUSE_LEAVE、MOUSE_UP、MOUSE_DOWNAFTER_TIMEOUT:遅延トリガー
ナビゲーションの種類:
NAVIGATE:移動先に移動SWAP:オーバーレイを置き換えOVERLAY:オーバーレイとして開くSCROLL_TO:指定位置までスクロールCHANGE_TO:状態を変更
2. オーバーレイの設定
フレームをオーバーレイとして表示する方法を制御します。
frame.overlayPositionType // 'CENTER' | 'TOP_LEFT' | 'TOP_CENTER' | etc.
frame.overlayBackground // How overlay obscures background
frame.overlayBackgroundInteraction // Click-through behavior
3. スクロール可能なフレーム
フレームのスクロール動作を設定します。
frame.overflowDirection = 'VERTICAL_SCROLLING' // or 'HORIZONTAL_SCROLLING'
frame.numberOfFixedChildren = 1 // First N children stay fixed during scroll
プラグイン開発
1. プラグインの構成
基本的なプラグインファイル:
my-plugin/
├── manifest.json # Plugin configuration
├── code.ts # Main plugin logic
└── ui.html # Optional UI
manifest.json:
{
"name": "My Plugin",
"id": "unique-plugin-id",
"api": "1.0.0",
"main": "code.js",
"ui": "ui.html",
"editorType": ["figma", "figjam"],
"documentAccess": "dynamic-page",
"networkAccess": {
"allowedDomains": ["api.example.com"]
}
}
2. プラグインのライフサイクル
// Initialize plugin
async function init() {
// Show UI (optional)
figma.showUI(__html__, {
width: 400,
height: 500,
title: "My Plugin",
themeColors: true
})
// Load saved preferences
const prefs = await figma.clientStorage.getAsync('preferences')
// Send initial data to UI
figma.ui.postMessage({
type: 'init',
data: prefs
})
}
// Handle UI messages
figma.ui.onmessage = async (msg) => {
if (msg.type === 'create-shapes') {
await createShapes(msg.count, msg.color)
}
if (msg.type === 'export') {
await exportSelection()
}
}
// Clean up on close
figma.on('close', () => {
console.log('Plugin closing...')
})
// Start plugin
init()
3. UI 通信
プラグインから UI にメッセージを送信:
figma.ui.postMessage({
type: 'selection-changed',
count: figma.currentPage.selection.length,
nodes: figma.currentPage.selection.map(n => ({
id: n.id,
name: n.name,
type: n.type
}))
})
UI でメッセージを受信(ui.html):
<script>
window.onmessage = (event) => {
const msg = event.data.pluginMessage
if (msg.type === 'selection-changed') {
document.getElementById('count').textContent = msg.count
}
}
// Send message to plugin
function createShapes() {
parent.postMessage({
pluginMessage: {
type: 'create-shapes',
count: 5,
color: { r: 1, g: 0, b: 0 }
}
}, '*')
}
</script>
4. イベントリスナー
ドキュメントと選択範囲の変更を監視します:
// Selection changes
figma.on('selectionchange', () => {
const selection = figma.currentPage.selection
console.log(`Selected ${selection.length} nodes`)
})
// Document changes
figma.on('documentchange', (event) => {
for (const change of event.documentChanges) {
if (change.type === 'CREATE') {
console.log(`Created: ${change.id}`)
}
if (change.type === 'DELETE') {
console.log(`Deleted: ${change.id}`)
}
if (change.type === 'PROPERTY_CHANGE') {
console.log(`Changed properties: ${change.properties.join(', ')}`)
}
}
})
// Page changes
figma.on('currentpagechange', () => {
console.log(`Now on page: ${figma.currentPage.name}`)
})
5. データストレージ
プラグインデータ(プラグイン専用):
// Store data on node
node.setPluginData('status', 'approved')
node.setPluginData('metadata', JSON.stringify({
author: 'John',
tags: ['important']
}))
// Retrieve data
const status = node.getPluginData('status')
const metadata = JSON.parse(node.getPluginData('metadata') || '{}')
// List all keys
const keys = node.getPluginDataKeys()
共有プラグインデータ(名前空間からアクセス可能):
// Store shared data
node.setSharedPluginData('com.example.plugin', 'version', '2.0')
// Retrieve shared data
const version = node.getSharedPluginData('com.example.plugin', 'version')
// List shared keys
const keys = node.getSharedPluginDataKeys('com.example.plugin')
クライアントストレージ(永続的な設定):
// Save preferences
await figma.clientStorage.setAsync('preferences', {
theme: 'dark',
lastUsed: new Date().toISOString()
})
// Load preferences
const prefs = await figma.clientStorage.getAsync('preferences')
// Delete data
await figma.clientStorage.deleteAsync('preferences')
// List all keys
const keys = await figma.clientStorage.keysAsync()
ノードの検索
1. findAllWithCriteria による最適化された検索
大規模なドキュメントで最速の方法(数百倍高速):
// Enable performance optimization
figma.skipInvisibleInstanceChildren = true
// Find by type
const textNodes = figma.currentPage.findAllWithCriteria({
types: ['TEXT']
})
// Find multiple types
const shapes = figma.currentPage.findAllWithCriteria({
types: ['RECTANGLE', 'ELLIPSE', 'POLYGON']
})
// Find nodes with plugin data
const nodesWithStatus = figma.currentPage.findAllWithCriteria({
pluginData: {
keys: ['status']
}
})
// Find nodes with shared plugin data
const nodesWithSharedData = figma.currentPage.findAllWithCriteria({
sharedPluginData: {
namespace: 'com.example.plugin',
keys: ['version']
}
})
// Combine criteria
const textWithData = figma.currentPage.findAllWithCriteria({
types: ['TEXT'],
pluginData: {} // Any plugin data
})
2. 従来の検索方法
// Find all matching nodes
const frames = figma.currentPage.findAll(node => node.type === 'FRAME')
// Find first match
const template = figma.currentPage.findOne(node =>
node.name.startsWith('Template')
)
// Find by ID
const node = await figma.getNodeByIdAsync('123:456')
// Recursive tree traversal
function walkTree(node: BaseNode) {
console.log(`${node.type}: ${node.name}`)
if ('children' in node) {
for (const child of node.children) {
walkTree(child)
}
}
}
walkTree(figma.currentPage)
エクスポートと画像の処理
1. ノードのエクスポート
// Export as PNG
const pngBytes = await node.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 2 } // 2x resolution
})
// Export as JPG
const jpgBytes = await node.exportAsync({
format: 'JPG',
constraint: { type: 'SCALE', value: 1 },
contentsOnly: false // Include background
})
// Export as SVG
const svgBytes = await node.exportAsync({
format: 'SVG',
svgIdAttribute: true,
svgOutlineText: false,
svgSimplifyStroke: true
})
// Export with fixed dimensions
const thumbnail = await node.exportAsync({
format: 'PNG',
constraint: { type: 'WIDTH', value: 200 }
})
// Export with height constraint
const preview = await node.exportAsync({
format: 'PNG',
constraint: { type: 'HEIGHT', value: 400 }
})
2. 画像の読み込み
// Load from URL
const image = await figma.createImageAsync('https://example.com/image.png')
const { width, height } = await image.getSizeAsync()
// Create rectangle with image
const rect = figma.createRectangle()
rect.resize(width, height)
rect.fills = [{
type: 'IMAGE',
imageHash: image.hash,
scaleMode: 'FILL' // or 'FIT', 'CROP', 'TILE'
}]
// Load from bytes
function loadFromBytes(bytes: Uint8Array) {
const image = figma.createImage(bytes)
return image
}
// Create from SVG string
const svgString = `
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#3366ff"/>
</svg>
`
const node = figma.createNodeFromSvg(svgString)
テキストの処理
1. テキストの基本
// Create text (MUST load font first)
const text = figma.createText()
await figma.loadFontAsync(text.fontName) // Load default font
// Set content
text.characters = 'Hello World'
// Change font
await figma.loadFontAsync({ family: 'Inter', style: 'Bold' })
text.fontName = { family: 'Inter', style: 'Bold' }
// Styling
text.fontSize = 24
text.lineHeight = { value: 150, unit: 'PERCENT' }
text.letterSpacing = { value: 0, unit: 'PIXELS' }
text.textAlignHorizontal = 'CENTER'
text.textAlignVertical = 'CENTER'
text.textCase = 'UPPER' // or 'LOWER', 'TITLE', 'ORIGINAL'
text.textDecoration = 'UNDERLINE' // or 'STRIKETHROUGH', 'NONE'
2. リッチテキストの書式設定
// Apply formatting to range
text.setRangeFontSize(0, 5, 32) // First 5 chars = 32px
text.setRangeFills(0, 5, [{
type: 'SOLID',
color: { r: 1, g: 0, b: 0 }
}])
// Get font at position
const fontName = text.getRangeFontName(0, 1)
const fontSize = text.getRangeFontSize(0, 1)
サイズ変更と変形
1. サイズ変更の方法
// Resize with constraints applied
node.resize(300, 200)
// Resize without constraints
node.resizeWithoutConstraints(300, 200)
// Scale proportionally
node.rescale(1.5) // 150% scale
2. 変形
// Position
node.x = 100
node.y = 200
// Relative to parent
console.log(node.relativeTransform) // [[1,0,x], [0,1,y]]
// Absolute position on page
console.log(node.absoluteTransform)
// Bounding box
const bounds = node.absoluteBoundingBox
// { x: number, y: number, width: number, height: number }
コラボレーション機能
1. コメント
プラグインは API 経由でコメントを作成できませんが、次の操作が可能です:
- コメントのあるフレームに移動する
- REST API 経由でコメントのメタデータを読み取る
- 通知をトリガーする
2. バージョン履歴
// Plugin data can track custom versioning
node.setPluginData('version', '2.1.0')
node.setPluginData('lastModified', new Date().toISOString())
node.setPluginData('modifiedBy', '[email protected]')
3. チームライブラリ
// Check if node is from external library
if (node.type === 'INSTANCE') {
const component = node.mainComponent
if (component && component.parent?.type === 'PAGE') {
// Component is from library
}
}
Context7 に基づくベストプラクティス
1. パフォーマンスの最適化
// ✅ Use findAllWithCriteria for large documents
const nodes = figma.currentPage.findAllWithCriteria({
types: ['TEXT']
})
// ❌ Avoid findAll for simple type searches
const nodes = figma.currentPage.findAll(n => n.type === 'TEXT')
// ✅ Enable invisible instance optimization
figma.skipInvisibleInstanceChildren = true
// ✅ Batch operations
const nodes = []
for (let i = 0; i < 100; i++) {
const rect = figma.createRectangle()
rect.x = i * 120
nodes.push(rect)
}
figma.currentPage.selection = nodes
figma.viewport.scrollAndZoomIntoView(nodes)
// ❌ Avoid individual viewport updates
for (let i = 0; i < 100; i++) {
const rect = figma.createRectangle()
figma.viewport.scrollAndZoomIntoView([rect]) // Slow!
}
2. フォントの読み込み
// ✅ Always load fonts before modifying text
const text = figma.createText()
await figma.loadFontAsync(text.fontName)
text.characters = 'Hello'
// ✅ Load font before changing fontName
await figma.loadFontAsync({ family: 'Roboto', style: 'Bold' })
text.fontName = { family: 'Roboto', style: 'Bold' }
// ❌ This will error
text.characters = 'Hello' // Error: font not loaded
3. エラー処理
try {
const node = await figma.getNodeByIdAsync(id)
if (!node) {
figma.notify('Node not found', { error: true })
return
}
// Process node
} catch (error) {
figma.notify(`Error: ${error.message}`, { error: true })
console.error(error)
}
4. メモリ管理
// ✅ Clean up large data
figma.on('close', () => {
// Clear large caches
cache.clear()
})
// ✅ Use async iteration for large datasets
async function processManyNodes() {
const nodes = figma.currentPage.findAllWithCriteria({ types: ['TEXT'] })
for (let i = 0; i < nodes.length; i++) {
await processNode(nodes[i])
// Yield to UI every 100 items
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 0))
}
}
}
5. 型安全性
// ✅ Type guards
if (node.type === 'TEXT') {
// TypeScript knows node is TextNode
console.log(node.characters)
}
if ('children' in node) {
// Node has children
node.children.forEach(child => console.log(child.name))
}
// ✅ Use specific types
function processText(node: TextNode) {
console.log(node.characters)
}
// ❌ Avoid any
function processNode(node: any) { // Bad!
console.log(node.characters) // Might error
}
一般的なワークフロー
1. デザインシステムのセットアップ
async function setupDesignSystem() {
// Create variable collection
const tokens = figma.variables.createVariableCollection('Design Tokens')
// Colors
const colors = {
'color/primary': { r: 0.2, g: 0.5, b: 1, a: 1 },
'color/secondary': { r: 0.5, g: 0.2, b: 0.8, a: 1 },
'color/success': { r: 0.2, g: 0.8, b: 0.3, a: 1 },
'color/error': { r: 0.9, g: 0.2, b: 0.2, a: 1 }
}
for (const [name, value] of Object.entries(colors)) {
const variable = figma.variables.createVariable(name, tokens, 'COLOR')
variable.setValueForMode(tokens.modes[0].modeId, value)
}
// Spacing
const spacing = [4, 8, 12, 16, 24, 32, 48, 64]
spacing.forEach((value, i) => {
const variable = figma.variables.createVariable(
`spacing/${i}`,
tokens,
'FLOAT'
)
variable.setValueForMode(tokens.modes[0].modeId, value)
})
// Create styles
const primaryStyle = figma.createPaintStyle()
primaryStyle.name = 'Color/Primary'
primaryStyle.paints = [{
type: 'SOLID',
color: colors['color/primary']
}]
figma.notify('Design system created!')
}
2. コンポーネントライブラリ
async function createButtonLibrary() {
// Create component set for variants
const buttons = figma.createComponentSet()
buttons.name = "Button"
// Primary variant
const primary = figma.createComponent()
primary.name = "Type=Primary, Size=Medium"
primary.resize(120, 40)
buttons.appendChild(primary)
// Secondary variant
const secondary = figma.createComponent()
secondary.name = "Type=Secondary, Size=Medium"
secondary.resize(120, 40)
buttons.appendChild(secondary)
// Large size
const large = figma.createComponent()
large.name = "Type=Primary, Size=Large"
large.resize(160, 48)
buttons.appendChild(large)
return buttons
}
3. 一括エクスポート
async function exportAllFrames() {
const frames = figma.currentPage.findAllWithCriteria({
types: ['FRAME']
})
const exports = []
for (const frame of frames) {
// Export 1x and 2x
const bytes1x = await frame.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 1 }
})
const bytes2x = await frame.exportAsync({
format: 'PNG',
constraint: { type: 'SCALE', value: 2 }
})
exports.push({
name: frame.name,
'1x': bytes1x,
'2x': bytes2x
})
}
// Send to UI for download
figma.ui.postMessage({
type: 'exports-ready',
exports
})
}
4. スタイルの同期
async function syncStyles() {
const paintStyles = await figma.getLocalPaintStylesAsync()
const textStyles = await figma.getLocalTextStylesAsync()
// Update all instances
const nodes = figma.currentPage.findAllWithCriteria({
types: ['RECTANGLE', 'TEXT']
})
for (const node of nodes) {
if (node.type === 'RECTANGLE') {
// Apply paint style based on name
const style = paintStyles.find(s => s.name === 'Brand/Primary')
if (style) {
node.fillStyleId = style.id
}
}
if (node.type === 'TEXT') {
// Apply text style
const style = textStyles.find(s => s.name === 'Body/Regular')
if (style) {
node.textStyleId = style.id
}
}
}
figma.notify('Styles synced!')
}
プラグイン UI のパターン
1. テーマ対応 UI
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
padding: 16px;
font-family: 'Inter', sans-serif;
background: var(--figma-color-bg);
color: var(--figma-color-text);
}
button {
background: var(--figma-color-bg-brand);
color: var(--figma-color-text-onbrand);
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: var(--figma-color-bg-brand-hover);
}
input {
background: var(--figma-color-bg);
color: var(--figma-color-text);
border: 1px solid var(--figma-color-border);
padding: 8px;
border-radius: 4px;
}
</style>
</head>
<body>
<h2>My Plugin</h2>
<input type="text" id="input" placeholder="Enter text...">
<button onclick="handleClick()">Create</button>
</body>
</html>
2. 読み込み状態
<div id="status">
<div class="spinner"></div>
<p>Processing...</p>
</div>
<script>
function showLoading() {
document.getElementById('status').style.display = 'block'
}
function hideLoading() {
document.getElementById('status').style.display = 'none'
}
async function process() {
showLoading()
parent.postMessage({ pluginMessage: { type: 'process' } }, '*')
}
window.onmessage = (event) => {
if (event.data.pluginMessage.type === 'complete') {
hideLoading()
}
}
</script>
FigJam 固有の機能
if (figma.editorType === 'figjam') {
// Create sticky note
const sticky = figma.createSticky()
sticky.x = 100
sticky.y = 100
await figma.loadFontAsync({ family: "Roboto", style: "Regular" })
sticky.text.characters = "TODO: Review designs"
// Create connector
const connector = figma.createConnector()
connector.connectorStart = {
endpointNodeId: sticky.id,
position: { x: 0, y: 0.5 }
}
// Create shape
const shape = figma.createShape()
shape.resize(100, 100)
shape.fills = [{ type: 'SOLID', color: { r: 1, g: 0.8, b: 0 } }]
}
高度なテクニック
1. スマートなコンポーネントの差し替え
async function swapComponents(oldComponent: ComponentNode, newComponent: ComponentNode) {
const instances = figma.currentPage.findAllWithCriteria({
types: ['INSTANCE']
})
let swapCount = 0
for (const instance of instances) {
if (instance.mainComponent?.id === oldComponent.id) {
await instance.swapAsync(newComponent)
swapCount++
}
}
figma.notify(`Swapped ${swapCount} instances`)
}
2. オートレイアウトの移行
function convertToAutoLayout(frame: FrameNode) {
// Store original positions
const childData = frame.children.map(child => ({
node: child,
x: child.x,
y: child.y
}))
// Enable auto layout
frame.layoutMode = 'VERTICAL'
frame.primaryAxisSizingMode = 'AUTO'
frame.counterAxisSizingMode = 'FIXED'
frame.itemSpacing = 16
frame.paddingLeft = 24
frame.paddingRight = 24
frame.paddingTop = 24
frame.paddingBottom = 24
// Configure children
frame.children.forEach(child => {
if ('layoutAlign' in child) {
child.layoutAlign = 'STRETCH'
}
})
}
3. レスポンシブなサイズ変更
function makeResponsive(frame: FrameNode) {
frame.layoutMode = 'VERTICAL'
frame.primaryAxisSizingMode = 'AUTO'
frame.counterAxisSizingMode = 'FIXED'
// Make children responsive
frame.children.forEach(child => {
if ('resize' in child) {
child.layoutAlign = 'STRETCH'
child.layoutGrow = 0
child.minWidth = 200
child.maxWidth = 800
}
})
}
テストとデバッグ
// Console logging
console.log('Selection:', figma.currentPage.selection)
console.error('Error occurred:', error)
// Notifications
figma.notify('Success!', { timeout: 2000 })
figma.notify('Error occurred', { error: true })
// Debugging helpers
function debugNode(node: SceneNode) {
console.log('Node Debug Info:')
console.log(' Type:', node.type)
console.log(' Name:', node.name)
console.log(' ID:', node.id)
console.log(' Position:', { x: node.x, y: node.y })
if ('width' in node) {
console.log(' Size:', { width: node.width, height: node.height })
}
if ('children' in node) {
console.log(' Children:', node.children.length)
}
}
リソース
- Figma Plugin API 公式ドキュメント:https://www.figma.com/plugin-docs/
- Context7 ライブラリ:/figma/plugin-typings(信頼スコア:9.8)
- プラグインのサンプル:https://github.com/figma/plugin-samples
- コミュニティフォーラム:https://forum.figma.com/
- Widget API:https://www.figma.com/widget-docs/
- REST API:https://www.figma.com/developers/api
まとめ
このスキルでは、次の内容を扱います:
- コンポーネントとインスタンスの管理
- オートレイアウトと制約
- 変数とデザイントークン
- スタイル(ペイント、テキスト、エフェクト)
- プロトタイピングとインタラクション
- プラグイン開発のパターン
- ノードの検索と操作
- エクスポートと画像の処理
- パフォーマンスの最適化
- Context7 の調査に基づくベストプラクティス
このスキルは、本番環境で利用できる Figma プラグインの構築、デザインワークフローの自動化、デザインシステムの管理、拡張性の高いコンポーネントライブラリの作成に活用できます。