
Pencil To Code
- 8 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
Pencil-to-code is a skill that exports a Pencil .pen design into production React and Tailwind component code.
About
Pencil-to-code exports a finalized Pencil .pen design into production React and Tailwind code. It reads the .pen frame tree, extracts design variables into a Tailwind theme, and maps node types to semantic HTML components. A developer uses it after a design direction is chosen to turn a mockup into implementation code.
- Exports Pencil .pen designs into React + Tailwind components
- Maps .pen variables to a Tailwind 4 @theme token system
- Converts node types (frame, text, ref, image) to semantic HTML
Pencil To Code by the numbers
- 8 all-time installs (skills.sh)
- Ranked #1,729 of 2,244 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
pencil-to-code capabilities & compatibility
- Capabilities
- design to code · component generation · token extraction
- Use cases
- frontend · ui design
What pencil-to-code says it does
Export Pencil .pen designs to production React/Tailwind code.
**Single concern**: Export → code. No design modifications.
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill pencil-to-codeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Convert a finalized Pencil .pen frame into React and Tailwind component code.
Who is it for?
Turning a finalized Pencil design into React and Tailwind components
Skip if: Modifying designs; it does export only, no design changes
When should I use this skill?
A finalized Pencil .pen design needs to be converted to implementation code
What you get
React component files plus a Tailwind theme derived from the .pen variables.
- React component files
- Tailwind theme configuration
By the numbers
- 8-point spacing grid mapping
- 7-row node-type to component mapping table
Files
Pencil to Code
Export Pencil .pen designs to production React/Tailwind code.
Interface
Input:
- Frame ID to export (or entire document)
- Target framework: React (default), Vue, HTML
- Optional: Component name, output path
Output:
- React component(s) with Tailwind classes
- Tailwind theme configuration (from .pen variables)
- Optional: Screenshot comparison for validation
Workflow
1. Read Design Structure
// Get full frame tree
mcp__pencil__batch_get({
filePath: "<path>.pen",
nodeIds: ["frameId"],
readDepth: 10,
resolveInstances: true,
resolveVariables: true
})
// Get design variables/theme
mcp__pencil__get_variables({ filePath: "<path>.pen" })2. Extract Design Tokens
Transform .pen variables → Tailwind theme:
/* From .pen variables */
@theme {
--color-primary: [from variables.colors.primary];
--color-background: [from variables.colors.background];
--font-sans: [from variables.fonts.body];
/* ... */
}Reference: references/token-extraction.md
3. Map Nodes to Components
| .pen Node Type | React Output |
|---|---|
frame with layout | <div className="flex ..."> |
frame with children | Component with children |
text | <p>, <h1-6>, or <span> |
ref (instance) | Reusable component |
rectangle | <div> with fill |
ellipse | <div className="rounded-full"> |
image | <img> or fill: url() |
Reference: references/node-mapping.md
4. Generate Code
Component structure:
// components/[ComponentName].tsx
import { cn } from "@/lib/utils"
interface [ComponentName]Props {
className?: string
// Extracted props from design
}
export function [ComponentName]({ className, ...props }: [ComponentName]Props) {
return (
<div className={cn("[tailwind classes]", className)}>
{/* Nested structure */}
</div>
)
}Tailwind mapping:
.pen property → Tailwind class
--------------------------------
fill: #000 → bg-black
layout: horizontal → flex flex-row
gap: 16 → gap-4
padding: [16,24,16,24] → py-4 px-6
fontSize: 24 → text-2xl
fontWeight: 700 → font-bold
cornerRadius: [8,8,8,8] → rounded-lg5. Validate (Optional)
// Screenshot the .pen frame
mcp__pencil__get_screenshot({ nodeId: "frameId" })
// Compare visually with rendered React
// (Manual step or browser automation)6. Output
## Generated Components
### [ComponentName]
- File: `components/[ComponentName].tsx`
- Props: [list extracted props]
### Theme Configuration
- File: `app/globals.css` (additions)
## Verification
Screenshot comparison: [attached]Translation Rules
Layout System
.pen Tailwind
--------------------------------------
layout: "vertical" → flex flex-col
layout: "horizontal" → flex flex-row
layout: "grid" → grid
alignItems: "center" → items-center
justifyContent: "center" → justify-center
gap: 8 → gap-2
gap: 16 → gap-4
gap: 24 → gap-6
width: "fill_container" → w-full
height: "fill_container" → h-fullSpacing (8-point grid)
.pen padding Tailwind
----------------------------------------
[8,8,8,8] → p-2
[16,16,16,16] → p-4
[16,24,16,24] → py-4 px-6
[24,32,24,32] → py-6 px-8Typography
.pen Tailwind
----------------------------------------
fontSize: 12 → text-xs
fontSize: 14 → text-sm
fontSize: 16 → text-base
fontSize: 20 → text-xl
fontSize: 24 → text-2xl
fontSize: 32 → text-3xl
fontSize: 48 → text-5xl
fontWeight: 400 → font-normal
fontWeight: 500 → font-medium
fontWeight: 600 → font-semibold
fontWeight: 700 → font-boldColors
.pen Tailwind
----------------------------------------
fill: "#FFFFFF" → bg-white
fill: "#000000" → bg-black
fill: variable → bg-[var(--color-name)]
textColor: "#6B7280" → text-gray-500
stroke: "#E5E5E5" → border-gray-200Border Radius
.pen cornerRadius Tailwind
----------------------------------------
[0,0,0,0] → rounded-none
[4,4,4,4] → rounded
[8,8,8,8] → rounded-lg
[16,16,16,16] → rounded-2xl
[9999,9999,9999,9999] → rounded-fullConstraints
- Single concern: Export → code. No design modifications.
- Tailwind 4 @theme: CSS-first token system
- React + TypeScript: Default output target
- Semantic HTML: Choose appropriate elements
- Accessibility: Include aria attributes where detectable
References
references/node-mapping.md— Complete node → component mappingreferences/token-extraction.md— Variable → theme extractiondesign-tokens/— Token system conventions
Integration
Called by:
design-explorationorchestrator (after direction selection)- Direct user invocation
Composes:
design-tokens(for theme structure)
{
"ownerId": "kn79rc63e233wzf8e3ys1r5hss80zkh6",
"slug": "pencil-to-code",
"version": "0.1.0",
"publishedAt": 1770778916378
}{
"slug": "pencil-to-code",
"name": "Pencil To Code",
"version": "0.1.0",
"installedAt": 1776152395942,
"source": "skillhub"
}Node Mapping Reference
Complete mapping from .pen node types to React/Tailwind output.
Frame Node
Most common node type. Maps to <div> with flex/grid layout.
Basic Frame
{
"type": "frame",
"layout": "vertical",
"gap": 16,
"padding": [24, 32, 24, 32]
}<div className="flex flex-col gap-4 py-6 px-8">
{children}
</div>Frame with Fill
{
"type": "frame",
"fill": "#1A1A1A",
"cornerRadius": [12, 12, 12, 12]
}<div className="bg-[#1A1A1A] rounded-xl">
{children}
</div>Frame with Border
{
"type": "frame",
"stroke": "#E5E5E5",
"strokeThickness": 1,
"cornerRadius": [8, 8, 8, 8]
}<div className="border border-gray-200 rounded-lg">
{children}
</div>Frame with Gradient Fill
{
"type": "frame",
"fill": {
"type": "linear",
"angle": 135,
"stops": [
{"position": 0, "color": "#1E3A5F"},
{"position": 1, "color": "#0A1628"}
]
}
}<div className="bg-gradient-to-br from-[#1E3A5F] to-[#0A1628]">
{children}
</div>Grid Frame
{
"type": "frame",
"layout": "grid",
"columns": 3,
"gap": 24
}<div className="grid grid-cols-3 gap-6">
{children}
</div>Text Node
Maps to semantic HTML elements based on context.
Heading Detection
{
"type": "text",
"content": "Hero Title",
"fontSize": 48,
"fontWeight": 700
}// fontSize >= 32 && fontWeight >= 600 → heading
<h1 className="text-5xl font-bold">Hero Title</h1>
// Use h2-h6 based on hierarchy position in documentBody Text
{
"type": "text",
"content": "Description paragraph...",
"fontSize": 16,
"fontWeight": 400,
"lineHeight": 1.6
}<p className="text-base font-normal leading-relaxed">
Description paragraph...
</p>Styled Text
{
"type": "text",
"content": "Label",
"fontSize": 14,
"fontWeight": 500,
"textColor": "#6B7280"
}<span className="text-sm font-medium text-gray-500">Label</span>Text with Custom Font
{
"type": "text",
"fontFamily": "Playfair Display",
"fontSize": 64
}// Add to Tailwind theme
// --font-display: "Playfair Display", serif;
<h1 className="font-display text-6xl">...</h1>Rectangle Node
Simple shape, maps to styled <div>.
{
"type": "rectangle",
"width": 100,
"height": 4,
"fill": "#2563EB",
"cornerRadius": [2, 2, 2, 2]
}<div className="w-[100px] h-1 bg-blue-600 rounded-sm" />Ellipse Node
Maps to <div> with rounded-full.
{
"type": "ellipse",
"width": 48,
"height": 48,
"fill": "#10B981"
}<div className="w-12 h-12 bg-emerald-500 rounded-full" />Image Node
Maps to <img> or background image.
As Element
{
"type": "image",
"src": "https://...",
"width": 400,
"height": 300
}<img
src="https://..."
alt=""
className="w-[400px] h-[300px] object-cover"
/>As Background (frame with image fill)
{
"type": "frame",
"fill": {"type": "image", "src": "..."},
"width": 400,
"height": 300
}<div
className="w-[400px] h-[300px] bg-cover bg-center"
style={{ backgroundImage: 'url(...)' }}
/>Ref Node (Component Instance)
Reusable component. Extract as separate component.
{
"type": "ref",
"ref": "ButtonComponent",
"descendants": {
"label": {"content": "Submit"}
}
}// 1. First, extract ButtonComponent from its definition
// 2. Use as:
<Button>Submit</Button>
// If customization needed:
<Button variant="primary">Submit</Button>Component Extraction
When a frame has reusable: true, extract as component:
Source (.pen)
{
"id": "ButtonDef",
"type": "frame",
"reusable": true,
"layout": "horizontal",
"alignItems": "center",
"padding": [12, 24, 12, 24],
"cornerRadius": [8, 8, 8, 8],
"fill": "#2563EB",
"children": [
{
"id": "label",
"type": "text",
"content": "Button",
"textColor": "#FFFFFF"
}
]
}Output (React)
// components/Button.tsx
interface ButtonProps {
children: React.ReactNode
className?: string
}
export function Button({ children, className }: ButtonProps) {
return (
<button
className={cn(
"flex items-center py-3 px-6 rounded-lg bg-blue-600 text-white",
className
)}
>
{children}
</button>
)
}Layout Algorithm
Absolute → Flex Conversion
When .pen uses absolute positioning (layout: "none") but children have logical arrangement, convert to flex:
// If children are vertically stacked with similar gaps
// Convert to flex-col with gap
{
"type": "frame",
"layout": "none",
"children": [
{"y": 0, ...},
{"y": 50, ...},
{"y": 100, ...}
]
}// Detect consistent 50px gaps → gap-[50px] or closest Tailwind
<div className="flex flex-col gap-12">...</div>Nested Flex
{
"type": "frame",
"layout": "horizontal",
"children": [
{"type": "frame", "layout": "vertical", ...},
{"type": "frame", "layout": "vertical", ...}
]
}<div className="flex flex-row">
<div className="flex flex-col">...</div>
<div className="flex flex-col">...</div>
</div>Semantic HTML Selection
| .pen Pattern | HTML Element |
|---|---|
| Large text (32px+), bold, at top | <h1> - <h3> |
| Medium text (20-28px), in section | <h4> - <h6> |
| Paragraph-length body text | <p> |
| Short labels or inline text | <span> |
| Frame with click handler noted | <button> |
| Frame wrapping navigation items | <nav> |
| Frame as page section | <section> |
| Frame as header area | <header> |
| Frame as footer area | <footer> |
Accessibility Annotations
Add accessibility attributes based on design patterns:
// Button-like frames
<button aria-label="Submit form">...</button>
// Icon-only elements
<span aria-hidden="true">...</span>
// Decorative images
<img alt="" role="presentation" />
// Interactive cards
<article role="article">...</article>Value Conversion Tables
Gap → Tailwind
| .pen | Tailwind |
|---|---|
| 4 | gap-1 |
| 8 | gap-2 |
| 12 | gap-3 |
| 16 | gap-4 |
| 20 | gap-5 |
| 24 | gap-6 |
| 32 | gap-8 |
| 40 | gap-10 |
| 48 | gap-12 |
Font Size → Tailwind
| .pen | Tailwind |
|---|---|
| 12 | text-xs |
| 14 | text-sm |
| 16 | text-base |
| 18 | text-lg |
| 20 | text-xl |
| 24 | text-2xl |
| 30 | text-3xl |
| 36 | text-4xl |
| 48 | text-5xl |
| 60 | text-6xl |
| 72 | text-7xl |
Width/Height
fill_container → w-full / h-full
fixed number → w-[Npx] / h-[Npx]
→ or closest Tailwind class (w-64, h-48, etc.)Related skills
FAQ
What frameworks does pencil-to-code output?
React with Tailwind by default, with Vue and HTML as target options.
Does it modify the design?
No. It is single-concern export to code and does not modify designs.