Design to Code について
Design to Codeは、Pencilの.penデザインをReactとTailwind CSSのコードに変換する手順を示すスキルです。デザインの確認、トークンの対応付け、コンポーネント生成、スクリーンショットによる照合を扱います。
主な機能
- 01Pencilノード構造の確認
- 02コンポーネントインスタンスと変数の解決
- 03デザイントークンのTailwindへの対応付け
- 04Reactコンポーネントの生成
- 05スクリーンショット比較による検証
活用シーン
- 01PencilレイアウトからReactページを作成
- 02デザイン変数をTailwind設定に変換
- 03再利用可能なUIコンポーネントの構築
- 04生成コードをデザインと照合
MCP 対応
Pencilファイルの確認とデザイン変数の取得に、`mcp__pencil__batch_get` と `mcp__pencil__get_variables` を使う方法を示します。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ gyejoon/ pencil-plugin --skill design-to-codeインストール後、Claude Code で/design-to-codeを入力して読み込まれたか確認します。
同梱ファイルreferences/tailwind-mapping.mdreferences/component-templates.md
詳細
デザインからコードへの変換
Pencil .pen ファイルのデザインを React + Tailwind CSS コードに変換するためのガイドラインを提供します。
変換ワークフロー
1. デザインの分析
対象ノードの構造を把握します。
mcp__pencil__batch_get(
filePath: "design.pen",
nodeIds: ["targetNodeId"],
readDepth: 5,
resolveInstances: true,
resolveVariables: true
)
resolveInstances: trueでコンポーネントインスタンスを展開して確認し、resolveVariables: trueで変数の値を実際の値として確認します。
2. 変数の抽出
デザイントークンをTailwind設定に変換します。
mcp__pencil__get_variables(filePath: "design.pen")
3. コードの生成
ノードの種類に応じて、適切なReactコンポーネントに変換します。
4. スクリーンショットの比較
生成されたコードを視覚的に検証します。
ノードとコンポーネントの対応関係
| Pencil ノード | React/HTML | Tailwind クラス |
|---|---|---|
| frame(layout: vertical) | <div> | flex flex-col |
| frame(layout: horizontal) | <div> | flex flex-row |
| frame(layout: grid) | <div> | grid grid-cols-N |
| text | <p>、<span>、<h1-6> | text-*、font-* |
| rectangle | <div> | rounded-*、bg-* |
| ref(Button) | <button> または <Button> | コンポーネントごと |
プロパティとTailwindの対応関係
レイアウト
| Pencil プロパティ | Tailwind |
|---|---|
layout: "horizontal" | flex flex-row |
layout: "vertical" | flex flex-col |
layout: "grid" | grid |
gap: 16 | gap-4 |
padding: 16 | p-4 |
padding: [16, 24, 16, 24] | py-4 px-6 |
alignItems: "center" | items-center |
justifyContent: "center" | justify-center |
justifyContent: "space-between" | justify-between |
サイズ
| Pencil プロパティ | Tailwind |
|---|---|
width: "fill_container" | w-full |
height: "fill_container" | h-full |
width: "hug_contents" | w-fit |
width: 320 | w-80 または w-[320px] |
タイポグラフィ
| Pencil プロパティ | Tailwind |
|---|---|
fontSize: 16 | text-base |
fontSize: 24 | text-2xl |
fontWeight: "bold" | font-bold |
fontWeight: "semibold" | font-semibold |
textAlign: "center" | text-center |
textColor: "#3B82F6" | text-blue-500 |
色
| Pencil の色 | Tailwind |
|---|---|
#3B82F6 | blue-500 |
#EF4444 | red-500 |
#22C55E | green-500 |
#F8FAFC | slate-50 |
#0F172A | slate-900 |
角丸
| Pencil プロパティ | Tailwind |
|---|---|
cornerRadius: 4 | rounded-sm |
cornerRadius: 8 | rounded-lg |
cornerRadius: 12 | rounded-xl |
cornerRadius: 9999 | rounded-full |
エフェクト
| Pencil プロパティ | Tailwind |
|---|---|
opacity: 0.5 | opacity-50 |
overflow: "hidden" | overflow-hidden |
| 影(小) | shadow-sm |
| 影(中) | shadow-md |
コード生成パターン
基本コンポーネント
Pencil ノード:
{
"type": "frame",
"layout": "vertical",
"padding": 24,
"gap": 16,
"fill": "#FFFFFF",
"cornerRadius": 12,
"children": [
{ "type": "text", "content": "Title", "fontSize": 24, "fontWeight": "bold" },
{ "type": "text", "content": "Description", "textColor": "#64748B" }
]
}
React + Tailwind:
function Card() {
return (
<div className="flex flex-col p-6 gap-4 bg-white rounded-xl">
<h2 className="text-2xl font-bold">Title</h2>
<p className="text-slate-500">Description</p>
</div>
)
}
インタラクティブコンポーネント
ボタンコンポーネント:
interface ButtonProps {
children: React.ReactNode
variant?: 'primary' | 'secondary'
onClick?: () => void
}
function Button({ children, variant = 'primary', onClick }: ButtonProps) {
const baseClasses = "flex items-center justify-center px-6 py-3 gap-2 rounded-lg font-medium transition-colors"
const variantClasses = {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-transparent border border-blue-500 text-blue-500 hover:bg-blue-50"
}
return (
<button
className={`${baseClasses} ${variantClasses[variant]}`}
onClick={onClick}
>
{children}
</button>
)
}
レイアウトコンポーネント
ページレイアウト:
function PageLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-screen">
{/* Sidebar */}
<aside className="flex flex-col w-64 p-6 gap-4 bg-slate-50 border-r border-slate-200">
<h1 className="text-xl font-bold">Logo</h1>
<nav className="flex flex-col gap-2">
<a href="#" className="px-4 py-2 rounded-lg hover:bg-slate-100">Dashboard</a>
<a href="#" className="px-4 py-2 rounded-lg hover:bg-slate-100">Settings</a>
</nav>
</aside>
{/* Main Content */}
<main className="flex-1 p-8">
{children}
</main>
</div>
)
}
変数からTailwind設定への変換
Pencil変数をtailwind.config.jsに変換します。
入力(Pencil変数)
{
"colors": {
"primary": { "500": "#3B82F6", "600": "#2563EB" },
"neutral": { "50": "#F8FAFC", "900": "#0F172A" }
},
"spacing": { "4": 16, "6": 24, "8": 32 },
"radii": { "md": 8, "lg": 12 }
}
出力(Tailwind設定)
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: {
500: '#3B82F6',
600: '#2563EB',
},
neutral: {
50: '#F8FAFC',
900: '#0F172A',
},
},
spacing: {
4: '16px',
6: '24px',
8: '32px',
},
borderRadius: {
md: '8px',
lg: '12px',
},
},
},
}
ベストプラクティス
コンポーネントの命名
- 意味のある名前を使用します(Card、Button、Header)
- PascalCaseを使用します
- ファイル名をコンポーネント名と一致させます
Propsの設計
- 必須propsと任意propsを区別します
- 適切なデフォルト値を設定します
- TypeScriptインターフェースを定義します
Tailwindの使用
- ユーティリティクラスを優先します
- 繰り返し使うパターンはコンポーネントとして抽出します
- 複雑な値には任意の値を使用します(
w-[320px]) @applyの使用は最小限にします
アクセシビリティ
- セマンティックなHTML要素を使用します
- ARIA属性を適切に追加します
- キーボードナビゲーションに対応します
- 色のコントラストを確認します
出力形式
単一のTSXファイル形式:
// ComponentName.tsx
import { useState } from 'react'
interface ComponentNameProps {
// props 정의
}
export function ComponentName({ ...props }: ComponentNameProps) {
// 상태 및 로직
return (
// JSX
)
}
関連リソース
参照ファイル
references/tailwind-mapping.md- PencilとTailwindの完全な対応表references/component-templates.md- 一般的なコンポーネントのテンプレート
コメント
Markdown 対応