关于 Design to Code
Design to Code 是一项指导将 Pencil .pen 设计转换为 React 和 Tailwind CSS 代码的技能,涵盖设计检查、设计令牌映射、组件生成和截图视觉比对。
主要功能
- 01检查 Pencil 节点结构
- 02解析组件实例与变量
- 03将设计令牌映射到 Tailwind
- 04生成 React 组件
- 05通过截图比对验证
适用场景
- 01将 Pencil 布局转为 React 页面
- 02把设计变量转换为 Tailwind 配置
- 03构建可复用 UI 组件
- 04对照设计检查生成代码
MCP 相关
该技能展示使用 `mcp__pencil__batch_get` 和 `mcp__pencil__get_variables` 检查 Pencil 文件并获取设计变量。
安装
复制下面的命令,在终端中运行。
$
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 用法
- 优先使用工具类
- 将重复模式提取为组件
- 复杂值使用 arbitrary values
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