
Pencil Design
- 1.8k installs
- 21 repo stars
- Updated April 11, 2026
- chiroro-jr/pencil-design-skill
pencil-design is an agent skill for Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Penci
About
Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs. Triggers on tasks involving Pencil, .pen files, design-to-code workflows, or UI design with the Pencil MCP tools. The pencil-design skill documents workflows and patterns from the repository SKILL.md. --- name: pencil-design description: Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs. Triggers on tasks involving Pencil, .pen files, design-to-code workflows, or UI design with the Pencil MCP tools. metadata: author: Nyasha Chiroro version: "1.0" --- # Pencil Design Skill Design production-quality UIs in Pencil and generate clean, maintainable code from them. This skill enforces best practices for design system reuse, variable usage, layout correctness, visual verification, and design-to-code workflows.
- Designing screens, pages, or components in a `.pen` file
- Generating code (React, Next.js, Vue, Svelte, HTML/CSS) from Pencil designs
- Building or extending a design system in Pencil
- Syncing design tokens between Pencil and code (Tailwind v4 `@theme`, shadcn/ui tokens)
- Importing existing code into Pencil designs
Pencil Design by the numbers
- 1,755 all-time installs (skills.sh)
- +12 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #308 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
pencil-design capabilities & compatibility
- Capabilities
- designing screens, pages, or components in a `.p · generating code (react, next.js, vue, svelte, ht · building or extending a design system in pencil · syncing design tokens between pencil and code (t · importing existing code into pencil designs
- Use cases
- documentation
What pencil-design says it does
--- name: pencil-design description: Design UIs in Pencil (.pen files) and generate production code from them.
Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs.
Triggers on tasks involving Pencil, .pen files, design-to-code workflows, or UI design with the Pencil MCP tools.
metadata: author: Nyasha Chiroro version: "1.0" --- # Pencil Design Skill Design production-quality UIs in Pencil and generate clean, maintainable code from them.
npx skills add https://github.com/chiroro-jr/pencil-design-skill --skill pencil-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.8k |
|---|---|
| repo stars | ★ 21 |
| Security audit | 3 / 3 scanners passed |
| Last updated | April 11, 2026 |
| Repository | chiroro-jr/pencil-design-skill ↗ |
What problem does pencil-design solve for developers using the documented workflows?
Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs. Triggers
Who is it for?
Developers working with pencil-design patterns described in the skill documentation.
Skip if: Skip when docs are empty or the task is outside the skill documented scope.
When should I use this skill?
Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs. Triggers
What you get
Grounded guidance and workflows from SKILL.md for pencil-design.
- .pen UI designs
- Framework component code
- Verified layout exports
By the numbers
- Supports 5 export targets: React, Next.js, Vue, Svelte, and HTML/CSS
- Skill metadata version is 1.0
Files
Pencil Design Skill
Design production-quality UIs in Pencil and generate clean, maintainable code from them. This skill enforces best practices for design system reuse, variable usage, layout correctness, visual verification, and design-to-code workflows.
When to Use This Skill
- Designing screens, pages, or components in a
.penfile - Generating code (React, Next.js, Vue, Svelte, HTML/CSS) from Pencil designs
- Building or extending a design system in Pencil
- Syncing design tokens between Pencil and code (Tailwind v4
@theme, shadcn/ui tokens) - Importing existing code into Pencil designs
- Working with any Pencil MCP tools (
pencil_batch_design,pencil_batch_get, etc.)
Critical Rules
These rules address the most common agent mistakes. Violating them produces designs that are inconsistent, hard to maintain, and generate poor code.
Rule 1: Always Reuse Design System Components
NEVER recreate a component from scratch when one already exists in the design file.
Before inserting any element, you MUST: 1. Call pencil_batch_get with patterns: [{ reusable: true }] to list all available reusable components 2. Search the results for a component that matches what you need (button, card, input, nav, etc.) 3. If a match exists, insert it as a ref instance using I(parent, { type: "ref", ref: "<componentId>" }) 4. Customize the instance by updating its descendants with U(instanceId + "/childId", { ... }) 5. Only create a new component from scratch if no suitable reusable component exists
See references/design-system-components.md for detailed workflow.
Rule 2: Always Use Variables Instead of Hardcoded Values
NEVER hardcode colors, border radius, spacing, or typography values when variables exist.
Before applying any style value, you MUST: 1. Call pencil_get_variables to read all defined design tokens 2. Map your intended values to existing variables (e.g., use primary not #3b82f6, use radius-md not 6) 3. Apply values using variable references, not raw values 4. When generating code, use Tailwind v4 semantic utility classes (e.g., bg-primary, text-foreground, rounded-md). NEVER use arbitrary value syntax (bg-[#3b82f6], text-[var(--primary)], rounded-[6px])
See references/variables-and-tokens.md for detailed workflow.
Rule 3: Prevent Text and Content Overflow
NEVER allow text or child elements to overflow their parent or the artboard.
For every text element and container: 1. Set appropriate text wrapping and truncation 2. Constrain widths to parent bounds, especially on mobile screens (typically 375px wide) 3. Use "fill_container" for width on text elements inside auto-layout frames 4. After inserting content, call pencil_snapshot_layout with problemsOnly: true to detect clipping/overflow 5. Fix any reported issues before proceeding
See references/layout-and-text-overflow.md for detailed workflow.
Rule 4: Visually Verify Every Section
NEVER skip visual verification after building a section or screen.
After completing each logical section (header, hero, sidebar, form, card grid, etc.): 1. Call pencil_get_screenshot on the section or full screen node 2. Analyze the screenshot for: alignment issues, spacing inconsistencies, text overflow, visual glitches, missing content 3. Call pencil_snapshot_layout with problemsOnly: true to catch clipping and overlap 4. Fix any issues found before moving to the next section 5. Take a final full-screen screenshot when the entire design is complete
See references/visual-verification.md for detailed workflow.
Rule 5: Reuse Existing Assets (Logos, Icons, Images)
NEVER generate a new logo or duplicate asset when one already exists in the document.
Before generating any image or logo: 1. Call pencil_batch_get and search for existing image/logo nodes by name pattern (e.g., patterns: [{ name: "logo|brand|icon" }]) 2. If a matching asset exists elsewhere in the document (another artboard/screen), copy it using the C() (Copy) operation 3. Only use the G() (Generate) operation for genuinely new images that don't exist anywhere in the document 4. For logos specifically: always copy from an existing instance, never regenerate
See references/asset-reuse.md for detailed workflow.
Rule 6: Always Load the frontend-design Skill
NEVER design in Pencil or generate code from Pencil without first loading the `frontend-design` skill.
The frontend-design skill provides the aesthetic direction and design quality standards that prevent generic, cookie-cutter UI. You MUST: 1. Load the frontend-design skill at the start of any Pencil design or code generation task 2. Follow its design thinking process: understand purpose, commit to a bold aesthetic direction, consider differentiation 3. Apply its guidelines on typography, color, motion, spatial composition, and visual details — both when designing in Pencil and when generating code from Pencil designs 4. Never produce generic AI aesthetics (overused fonts, cliched color schemes, predictable layouts)
This applies to both directions:
- Pencil design tasks: Use the skill's aesthetic guidelines to inform layout, typography, color, and composition choices in the .pen file
- Code generation from Pencil: Use the skill's guidelines to ensure the generated code includes distinctive typography, intentional color themes, motion/animations, and polished visual details — not just a mechanical translation of the design tree
Design Workflow
Starting a New Design
0. Load `frontend-design` skill -> Get aesthetic direction and design quality standards
1. pencil_get_editor_state -> Understand file state, get schema
2. pencil_batch_get (reusable) -> Discover design system components
3. pencil_get_variables -> Read design tokens
4. pencil_get_guidelines -> Get relevant design rules
5. pencil_get_style_guide_tags -> (optional) Get style inspiration
6. pencil_get_style_guide -> (optional) Apply style direction
7. pencil_find_empty_space_on_canvas -> Find space for new screen
8. pencil_batch_design -> Build the design (section by section)
9. pencil_get_screenshot -> Verify each section visually
10. pencil_snapshot_layout -> Check for layout problemsBuilding Section by Section
For each section of a screen (header, content area, footer, sidebar, etc.):
1. Plan - Identify which design system components to reuse 2. Build - Insert components as ref instances, apply variables for styles 3. Verify - Screenshot the section + check layout for problems 4. Fix - Address any overflow, alignment, or spacing issues 5. Proceed - Move to the next section only after verification passes
Design-to-Code Workflow
See references/design-to-code-workflow.md for the complete workflow. See references/tailwind-shadcn-mapping.md for the full Pencil-to-Tailwind mapping table. See references/responsive-breakpoints.md for multi-artboard responsive code generation.
Summary: 1. Load the frontend-design skill for aesthetic direction 2. Call pencil_get_guidelines with topic "code" and "tailwind" 3. Call pencil_get_variables to map design tokens to Tailwind @theme declarations 4. Read the design tree with pencil_batch_get 5. Map reusable Pencil components to shadcn/ui components (Button, Card, Input, etc.) 6. Generate code using semantic Tailwind classes (bg-primary, rounded-md), never arbitrary values 7. Apply frontend-design guidelines: distinctive typography, intentional color, motion, spatial composition 8. Use CVA for custom component variants, cn() for class merging, Lucide for icons
MCP Tool Quick Reference
| Tool | When to Use |
|---|---|
pencil_get_editor_state | First call - understand file state and get .pen schema |
pencil_batch_get | Read nodes, search for components (reusable: true), inspect structure |
pencil_batch_design | Insert, copy, update, replace, move, delete elements; generate images |
pencil_get_variables | Read design tokens (colors, radius, spacing, fonts) |
pencil_set_variables | Create or update design tokens |
pencil_get_screenshot | Visual verification of any node |
pencil_snapshot_layout | Detect clipping, overflow, overlapping elements |
pencil_get_guidelines | Get design rules for: code, table, tailwind, landing-page, design-system |
pencil_find_empty_space_on_canvas | Find space for new screens/frames |
pencil_get_style_guide_tags | Browse available style directions |
pencil_get_style_guide | Get specific style inspiration |
pencil_search_all_unique_properties | Audit property values across the document |
pencil_replace_all_matching_properties | Bulk update properties (e.g., swap colors) |
pencil_open_document | Open a .pen file or create a new document |
Common Mistakes to Avoid
| Mistake | Correct Approach |
|---|---|
| Creating a button from scratch | Search for existing button component, insert as ref |
Using fill: "#3b82f6" | Use the variable: reference primary or the corresponding variable |
Using cornerRadius: 8 | Use the variable: reference radius-md or the corresponding variable |
Generating bg-[#3b82f6] in code | Use semantic Tailwind class: bg-primary |
Generating text-[var(--primary)] in code | Use semantic Tailwind class: text-primary |
Generating rounded-[6px] in code | Use semantic Tailwind class: rounded-md |
Using var(--primary) in className | Use semantic Tailwind class: bg-primary or text-primary |
| Not checking for overflow | Call pencil_snapshot_layout(problemsOnly: true) after every section |
| Skipping screenshots | Call pencil_get_screenshot after every section |
| Generating a new logo | Copy existing logo from another artboard with C() |
| Building entire screen, then checking | Build and verify section by section |
Ignoring pencil_get_guidelines | Always call it for the relevant topic before starting |
Using tailwind.config.ts | Use CSS @theme block (Tailwind v4) |
| Using Material Icons in code | Map to Lucide icons (<Search />, <ArrowRight />, etc.) |
Skipping frontend-design skill | Always load it before designing in Pencil or generating code |
| Generic AI aesthetics (Inter font, purple gradients) | Follow frontend-design guidelines for distinctive, intentional design |
Resources
Pencil Design Skill
⚠️ Repository moved: This project is now maintained at
https://github.com/chiroro-jr/skills/pencil-design.>
Install it with:
npx skills add https://github.com/chiroro-jr/skills --skill pencil-designAn Agent Skills skill for designing production-quality UIs in Pencil and generating clean, maintainable code.
Overview
This skill helps AI agents work effectively with Pencil (.pen files) — a vector design tool that integrates into IDEs. It enforces best practices for:
- Design system reuse — Using existing components instead of recreating them
- Design tokens — Using variables instead of hardcoded values
- Layout correctness — Preventing overflow and visual defects
- Visual verification — Taking screenshots to verify designs
- Code generation — Producing React/Next.js + Tailwind v4 + shadcn/ui code
Installation
Via opencode CLI
npx skills add https://github.com/chiroro-jr/skills --skill pencil-designManual Installation
1. Clone the new repository into your skills directory:
git clone https://github.com/chiroro-jr/skills.git ~/.agents/skills/chiroro-skills2. Register the skill in .skill-lock.json:
"pencil-design": {
"source": "chiroro-jr/skills",
"sourceType": "github",
"sourceUrl": "https://github.com/chiroro-jr/skills.git",
"skillPath": "pencil-design/SKILL.md",
"skillFolderHash": "<latest-commit-hash>",
"installedAt": "<timestamp>",
"updatedAt": "<timestamp>"
}When to Use
Load this skill when:
- Designing screens, pages, or components in a
.penfile - Generating code (React, Next.js, Vue, Svelte, HTML/CSS) from Pencil designs
- Building or extending a design system in Pencil
- Syncing design tokens between Pencil and code
- Importing existing code into Pencil designs
- Working with any Pencil MCP tools
The 6 Critical Rules
This skill enforces 6 rules that prevent common agent mistakes:
1. Always Reuse Design System Components
Never recreate a component from scratch when one exists. Search for reusable components (reusable: true) and insert them as ref instances.
2. Always Use Variables Instead of Hardcoded Values
Never hardcode colors, border radius, spacing, or typography. Use design tokens and generate semantic Tailwind classes (bg-primary, not bg-[#3b82f6]).
3. Prevent Text and Content Overflow
Never allow content to overflow its parent. Set appropriate constraints, use fill_container widths, and verify with pencil_snapshot_layout(problemsOnly: true).
4. Visually Verify Every Section
Never skip screenshots. Take pencil_get_screenshot after each section and analyze for alignment, spacing, and visual issues.
5. Reuse Existing Assets (Logos, Icons, Images)
Never generate a new logo when one exists. Search the document and copy existing assets with C() instead of regenerating.
6. Always Load the frontend-design Skill
Never design without aesthetic direction. Load the frontend-design skill first to get bold, intentional design guidelines and avoid generic AI aesthetics.
Workflow
Starting a New Design
0. Load `frontend-design` skill -> Get aesthetic direction
1. pencil_get_editor_state -> Understand file state
2. pencil_batch_get (reusable) -> Discover components
3. pencil_get_variables -> Read design tokens
4. pencil_get_guidelines -> Get design rules
5. pencil_batch_design -> Build the design
6. pencil_get_screenshot -> Verify visually
7. pencil_snapshot_layout -> Check for problemsDesign-to-Code
1. Load the frontend-design skill 2. Call pencil_get_guidelines with topic "code" and "tailwind" 3. Call pencil_get_variables to map tokens to Tailwind @theme 4. Read the design tree with pencil_batch_get 5. Map Pencil components to shadcn/ui components 6. Generate code using semantic Tailwind classes 7. Apply frontend-design guidelines for typography, color, motion
Target Stack
When generating code from Pencil designs, this skill targets:
- Framework: React/Next.js
- Styling: Tailwind CSS v4 (
@themeblocks, nottailwind.config.ts) - Components: shadcn/ui
- Language: TypeScript
- Icons: Lucide React
- Utilities: CVA (variants),
cn()from@/lib/utils - React Version: 19 (ref as prop, no
forwardRef)
File Structure
pencil-design/
├── SKILL.md # Main skill file with 6 rules
└── references/
├── design-system-components.md # Rule 1: Component reuse workflow
├── variables-and-tokens.md # Rule 2: Variable usage and Tailwind mapping
├── layout-and-text-overflow.md # Rule 3: Overflow prevention
├── visual-verification.md # Rule 4: Screenshot verification
├── asset-reuse.md # Rule 5: Asset copying rules
├── design-to-code-workflow.md # Complete code generation guide
├── tailwind-shadcn-mapping.md # Quick-reference mapping tables
└── responsive-breakpoints.md # Multi-artboard responsive patternsExample Usage
Designing in Pencil
// Discover available components
const components = await pencil_batch_get({
filePath: "design.pen",
patterns: [{ reusable: true }]
});
// Read design tokens
const tokens = await pencil_get_variables({ filePath: "design.pen" });
// Insert a component instance
btn = I("parentId", { type: "ref", ref: "button-primary", width: "fill_container" });
// Customize it
U(btn + "/label", { content: "Submit" });
// Verify
await pencil_get_screenshot({ filePath: "design.pen", nodeId: btn });Generating Code
// The skill guides code generation to produce:
// ❌ Never this:
<div className="bg-[#3b82f6] rounded-[6px] p-[24px]">
// ✅ Always this:
<div className="bg-primary rounded-md p-6">Resources
License
MIT
Author
Nyasha Chiroro (@chiroro-jr)
Asset Reuse
Why This Matters
AI image generation is non-deterministic. Every time you generate a logo, it will look different. If a project already has a logo on one screen, generating a new one for another screen creates visual inconsistency - the app appears to have two different brands.
The same applies to:
- Product images used across multiple screens
- Illustrations or decorative graphics
- Brand elements (logos, wordmarks, icons)
- Profile photos or avatars used in different contexts
Step-by-Step: Finding and Reusing Assets
Step 1: Search for Existing Assets
Before generating any image, search the document for existing ones:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [{ name: "logo" }],
searchDepth: 5
})Search with multiple name patterns to cast a wide net:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [
{ name: "logo" },
{ name: "brand" },
{ name: "icon" },
{ name: "image" }
],
searchDepth: 5
})You can also search by node type for frames that might contain image fills:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [{ type: "frame", name: "logo|brand|hero" }],
searchDepth: 5
})Step 2: Copy the Existing Asset
When you find an existing logo or image asset, copy it:
// Copy the logo from another artboard into the current screen
logoCopy=C("existingLogoNodeId", "targetParentId", { width: 120, height: 40 })The Copy operation (C()) creates a duplicate of the node, preserving its image fill, styling, and structure.
For reusable components that contain logos (e.g., a header component with a built-in logo), insert the component as a ref:
// Insert the entire header component which already contains the logo
header=I("screenId", { type: "ref", ref: "HeaderComponent", width: "fill_container" })Step 3: Adjust Size and Position
After copying, you may need to resize:
U("copiedLogoId", { width: 100, height: 32 })Or adjust position within the new context.
When to Generate New Images
Only use the G() (Generate) operation when:
1. No similar asset exists anywhere in the document 2. The image is genuinely unique to this screen (e.g., a specific hero photo, a unique illustration) 3. You're building the first screen and no assets exist yet
// Only when no existing asset matches
heroImg=I("heroSection", { type: "frame", name: "Hero Image", width: "fill_container", height: 400 })
G(heroImg, "stock", "modern office workspace")Logo-Specific Rules
Logos have the strictest reuse requirements:
1. ALWAYS search first - A logo should exist if any other screen in the document has been designed 2. ALWAYS copy - Never generate a new logo if one exists. Generated logos will never match. 3. Keep proportions - When resizing a copied logo, maintain aspect ratio 4. Check both artboards and components - The logo might be inside a reusable header/navbar component
Decision Tree
Need an image/logo?
├── Is it a logo or brand element?
│ ├── Does one exist elsewhere in the doc? -> COPY IT
│ └── First screen, nothing exists? -> Generate or ask user for asset
├── Is it a product photo / hero image?
│ ├── Same image used on another screen? -> COPY IT
│ └── Unique to this screen? -> Generate with G() or use stock
└── Is it an icon?
├── Exists in design system components? -> Use the component ref
└── New icon needed? -> Use icon_font type or generateChecklist
Before generating any image:
- [ ] Have I searched for existing logos/images with
pencil_batch_get? - [ ] Have I searched using name patterns like
logo,brand,image,hero? - [ ] Have I checked if a reusable component (navbar, header) already contains the logo?
- [ ] Am I copying existing assets instead of regenerating them?
- [ ] For logos specifically: am I absolutely sure no logo exists in the document?
See Also
- design-system-components.md — Check reusable components that may contain logos/icons
- visual-verification.md — Verify copied assets look correct after placement
Design System Components
Why This Matters
Pencil design files often contain a design system with reusable components (buttons, cards, inputs, navbars, etc.) marked with reusable: true. These are equivalent to Figma components or React components. When you recreate a component from scratch instead of reusing the existing one:
- The design becomes inconsistent (slightly different padding, colors, fonts)
- Changes to the design system don't propagate to your new element
- Code generation produces duplicated, non-DRY component code
- The design file grows with redundant elements
Step-by-Step: Discovering and Using Components
Step 1: List All Reusable Components
Always do this at the start of any design task:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [{ reusable: true }],
readDepth: 2,
searchDepth: 3
})This returns all components with their children (depth 2), searched up to 3 levels deep. You'll see components like:
{
"id": "btn-primary",
"name": "Button",
"type": "frame",
"reusable": true,
"children": [
{ "id": "btn-label", "type": "text", "content": "Button" }
]
}Step 2: Identify the Right Component
Look for components that match your need:
- Name matching: "Button", "Card", "Input", "NavBar", "Avatar", etc.
- Structure matching: If you need a card with image + title + description, look for a component with that structure
- Variant matching: Some design systems have multiple variants (e.g., "Button Primary", "Button Secondary")
Step 3: Insert as a Ref Instance
Use the component's ID as the ref value:
// Insert a button instance
btn=I("parentFrameId", { type: "ref", ref: "btn-primary", width: "fill_container" })This creates a connected instance. Edits to the main component will propagate to this instance.
Step 4: Customize the Instance
Override specific properties on the instance's descendants:
// Change the button label text
U(btn+"/btn-label", { content: "Submit" })For deeper customization, use the Update operation on nested paths:
// Update nested content: instanceId/descendantId
U(btn+"/icon-container/icon", { content: "arrow_forward" })Step 5: Replace Slots
If a component has placeholder/slot areas, use Replace to swap content:
// Replace a slot inside the component instance
newContent=R(btn+"/content-slot", { type: "text", content: "Custom Content" })When to Create a New Component
Only create a new component from scratch when:
1. No similar component exists in the design system after checking reusable: true 2. The existing component is fundamentally different (not just a color or text change) 3. You're building a new design system from an empty file
When creating a new component, consider making it reusable for future use by setting reusable: true.
Design System Discovery Checklist
Before designing any element, answer these questions:
- [ ] Have I called
pencil_batch_getwith{ reusable: true }to list components? - [ ] Have I checked if a matching component exists for: buttons, inputs, cards, navbars, headers, footers, modals, badges, avatars, tables?
- [ ] Am I inserting components as
refinstances (not recreating the structure)? - [ ] Am I customizing instances via
U()on descendant paths (not replacing the whole thing)?
Common Design System Components to Look For
| Need | Search for names containing |
|---|---|
| Button | button, btn, cta |
| Text input | input, field, text-field |
| Card | card, tile, panel |
| Navigation | nav, navbar, sidebar, menu |
| Header | header, topbar, appbar |
| Footer | footer, bottom-bar |
| Modal/Dialog | modal, dialog, sheet |
| Badge/Tag | badge, tag, chip, label |
| Avatar | avatar, profile-pic |
| Table row | row, table-row, list-item |
| Icon | icon, symbol |
| Checkbox/Radio | checkbox, radio, toggle, switch |
| Select/Dropdown | select, dropdown, picker |
| Tab | tab, tab-bar, segment |
See Also
- variables-and-tokens.md — Use variables when styling component instances
- design-to-code-workflow.md — Map reusable components to shadcn/ui
- tailwind-shadcn-mapping.md — Pencil component -> shadcn/ui component table
Design-to-Code Workflow
Overview
Pencil enables a two-way sync between design and code. This reference covers the complete workflow for generating clean, production-ready React + Tailwind v4 + shadcn/ui code from Pencil designs.
Target stack: React/Next.js, TypeScript, Tailwind CSS v4, shadcn/ui, Lucide icons, CVA for variants.
Step 1: Load the frontend-design Skill
MANDATORY. Before any design or code generation work, load the frontend-design skill. This provides:
- Aesthetic direction: bold, intentional design choices (not generic AI slop)
- Typography guidelines: distinctive font pairings, not overused defaults
- Color and theme guidelines: cohesive palettes with dominant colors and sharp accents
- Motion and animation: purposeful transitions and micro-interactions
- Spatial composition: unexpected layouts, asymmetry, generous negative space
Apply these guidelines both when designing in Pencil and when translating the design to code. The generated code should feel designed, not just mechanically translated from a node tree.
Step 2: Read Design Guidelines
Before generating any code, call the relevant Pencil guidelines:
pencil_get_guidelines({ topic: "code" })
pencil_get_guidelines({ topic: "tailwind" })These return the specific rules for translating .pen design properties into code.
Step 3: Read Design Tokens
pencil_get_variables({ filePath: "path/to/file.pen" })Map every Pencil variable to its Tailwind v4 @theme declaration and utility class. See variables-and-tokens.md for the full mapping table.
Key principle: Pencil variable names map 1:1 to Tailwind semantic utilities. No arbitrary values.
| Pencil Variable | @theme Declaration | Utility Class |
|---|---|---|
primary | --color-primary | bg-primary / text-primary |
primary-foreground | --color-primary-foreground | text-primary-foreground |
background | --color-background | bg-background |
foreground | --color-foreground | text-foreground |
border | --color-border | border-border |
radius-md | --radius-md | rounded-md |
muted | --color-muted | bg-muted |
muted-foreground | --color-muted-foreground | text-muted-foreground |
Step 4: Read the Design Tree
pencil_batch_get({
filePath: "path/to/file.pen",
nodeIds: ["screenId"],
readDepth: 5
})Use sufficient readDepth to see the full structure. For complex screens, you may need to read specific subtrees separately.
Step 5: Map Design Components to shadcn/ui Components
Identify reusable components (reusable: true nodes) and map them to shadcn/ui components:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [{ reusable: true }],
readDepth: 3
})Pencil-to-shadcn/ui Component Mapping
| Pencil Component Name | shadcn/ui Component | Import |
|---|---|---|
| Button / Btn | <Button> | @/components/ui/button |
| Card / Tile / Panel | <Card>, <CardHeader>, <CardContent>, <CardFooter> | @/components/ui/card |
| Input / TextField | <Input> | @/components/ui/input |
| Select / Dropdown | <Select>, <SelectTrigger>, <SelectContent>, <SelectItem> | @/components/ui/select |
| Checkbox | <Checkbox> | @/components/ui/checkbox |
| Switch / Toggle | <Switch> | @/components/ui/switch |
| Badge / Tag / Chip | <Badge> | @/components/ui/badge |
| Avatar | <Avatar>, <AvatarImage>, <AvatarFallback> | @/components/ui/avatar |
| Dialog / Modal | <Dialog>, <DialogTrigger>, <DialogContent> | @/components/ui/dialog |
| Tabs / TabBar | <Tabs>, <TabsList>, <TabsTrigger>, <TabsContent> | @/components/ui/tabs |
| Table / DataTable | <Table>, <TableHeader>, <TableRow>, <TableCell> | @/components/ui/table |
| Tooltip | <Tooltip>, <TooltipTrigger>, <TooltipContent> | @/components/ui/tooltip |
| Label | <Label> | @/components/ui/label |
| Separator / Divider | <Separator> | @/components/ui/separator |
If a Pencil component has no shadcn/ui equivalent, create a custom component following the same conventions (CVA variants, cn() utility, ref forwarding via React 19 prop).
Querying the shadcn/ui Registry
When a Pencil component doesn't have an obvious match in the table above, query the shadcn/ui registry to check for available components:
shadcn_search_items_in_registries({
registries: ["@shadcn"],
query: "data table" // search by the Pencil component's function
})Use shadcn_view_items_in_registries to inspect a component's files and API:
shadcn_view_items_in_registries({
items: ["@shadcn/data-table"]
})Use shadcn_get_item_examples_from_registries to see usage patterns:
shadcn_get_item_examples_from_registries({
registries: ["@shadcn"],
query: "data-table-demo"
})If a matching registry component exists, install it with shadcn_get_add_command_for_items and use it instead of building from scratch.
Instances (ref nodes) become usages of these components with their overridden props.
Step 6: Generate Code
CSS Setup (app.css / globals.css)
Generate the @theme block from Pencil design tokens. Use the --color-* namespace for colors and --radius-* for border radii so Tailwind auto-generates semantic utilities:
@import "tailwindcss";
@theme {
/* Colors from Pencil variables */
--color-background: oklch(100% 0 0);
--color-foreground: oklch(14.5% 0.025 264);
--color-primary: oklch(14.5% 0.025 264);
--color-primary-foreground: oklch(98% 0.01 264);
--color-secondary: oklch(96% 0.01 264);
--color-secondary-foreground: oklch(14.5% 0.025 264);
--color-muted: oklch(96% 0.01 264);
--color-muted-foreground: oklch(46% 0.02 264);
--color-accent: oklch(96% 0.01 264);
--color-accent-foreground: oklch(14.5% 0.025 264);
--color-destructive: oklch(53% 0.22 27);
--color-destructive-foreground: oklch(98% 0.01 264);
--color-card: oklch(100% 0 0);
--color-card-foreground: oklch(14.5% 0.025 264);
--color-border: oklch(91% 0.01 264);
--color-ring: oklch(14.5% 0.025 264);
/* Radius from Pencil variables */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
}
/* Dark mode */
@custom-variant dark (&:where(.dark, .dark *));
.dark {
--color-background: oklch(14.5% 0.025 264);
--color-foreground: oklch(98% 0.01 264);
/* ... other dark overrides from Pencil theme variables */
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground antialiased;
}
}Key rules for `@theme` block:
- Colors MUST use the
--color-*prefix so Tailwind generatesbg-*,text-*,border-*utilities - Radius MUST use the
--radius-*prefix so Tailwind generatesrounded-*utilities - Prefer OKLCH color format for better perceptual uniformity
- If the Pencil file has hex values, convert them to OKLCH
Component Code
For each reusable Pencil component, generate a component file using the CVA + cn() pattern:
// components/ui/status-badge.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const statusBadgeVariants = cva(
"inline-flex items-center rounded-md px-2 py-1 text-xs font-medium",
{
variants: {
status: {
active: "bg-primary text-primary-foreground",
inactive: "bg-muted text-muted-foreground",
error: "bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
status: "active",
},
}
)
interface StatusBadgeProps
extends React.HTMLAttributes<HTMLSpanElement>,
VariantProps<typeof statusBadgeVariants> {}
export function StatusBadge({ className, status, ...props }: StatusBadgeProps) {
return (
<span className={cn(statusBadgeVariants({ status, className }))} {...props} />
)
}Notice:
- All colors use semantic Tailwind classes (
bg-primary,text-muted-foreground) - All radii use semantic classes (
rounded-md) - No arbitrary values anywhere
- Uses
cn()from@/lib/utilsfor class merging - React 19 style (no
forwardRef)
Page/Screen Code
For the screen layout, generate a page component that:
- Imports shadcn/ui components matching the Pencil design system components
- Uses semantic Tailwind classes for all style values
- Matches the Pencil node tree structure (vertical/horizontal → flex-col/flex-row)
// app/dashboard/page.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ArrowRight, Search } from "lucide-react"
export default function DashboardPage() {
return (
<div className="flex min-h-screen flex-col bg-background">
{/* Header - from Pencil NavBar component */}
<header className="flex items-center justify-between border-b border-border px-6 py-4">
<h1 className="text-lg font-semibold text-foreground">Dashboard</h1>
<div className="relative">
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input className="pl-9" placeholder="Search..." />
</div>
</header>
{/* Content - from Pencil layout */}
<main className="flex-1 p-6">
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<Card>
<CardHeader>
<CardTitle className="text-card-foreground">Revenue</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold text-foreground">$45,231</p>
<p className="text-sm text-muted-foreground">+20% from last month</p>
</CardContent>
</Card>
{/* ... more cards */}
</div>
<Button className="mt-6">
View Details <ArrowRight className="ml-2 size-4" />
</Button>
</main>
</div>
)
}Step 7: Sync Variables Back (Optional)
If the design tokens were updated in code, sync them back:
pencil_set_variables({
filePath: "path/to/file.pen",
variables: { ... }
})Responsive Code from Multi-Artboard Designs
If the Pencil file has artboards at multiple widths (e.g., 375px mobile, 768px tablet, 1280px desktop):
1. Read all artboards and compare their structures 2. Generate mobile-first code (base styles match the smallest artboard) 3. Add Tailwind breakpoint prefixes (md:, lg:, xl:) for larger layouts 4. Never hardcode artboard pixel widths — use w-full, max-w-7xl, responsive grid columns
See responsive-breakpoints.md for the complete artboard-to-breakpoint mapping, responsive patterns, and anti-patterns.
Code Generation Rules
Layout Mapping (Pencil -> Tailwind)
| Pencil Property | Tailwind Class |
|---|---|
layout: "vertical" | flex flex-col |
layout: "horizontal" | flex flex-row or flex |
gap: 4 | gap-1 |
gap: 8 | gap-2 |
gap: 12 | gap-3 |
gap: 16 | gap-4 |
gap: 20 | gap-5 |
gap: 24 | gap-6 |
gap: 32 | gap-8 |
padding: 8 | p-2 |
padding: 12 | p-3 |
padding: 16 | p-4 |
padding: 20 | p-5 |
padding: 24 | p-6 |
padding: 32 | p-8 |
paddingLeft: 16, paddingRight: 16 | px-4 |
paddingTop: 24, paddingBottom: 24 | py-6 |
width: "fill_container" | w-full or flex-1 |
height: "fill_container" | h-full or flex-1 |
cornerRadius (via radius-md var) | rounded-md |
alignItems: "center" | items-center |
alignItems: "start" | items-start |
alignItems: "end" | items-end |
justifyContent: "center" | justify-center |
justifyContent: "space-between" | justify-between |
justifyContent: "end" | justify-end |
Typography Mapping (Pencil -> Tailwind)
| Pencil Property | Tailwind Class |
|---|---|
fontSize: 12 | text-xs |
fontSize: 14 | text-sm |
fontSize: 16 | text-base |
fontSize: 18 | text-lg |
fontSize: 20 | text-xl |
fontSize: 24 | text-2xl |
fontSize: 30 | text-3xl |
fontSize: 36 | text-4xl |
fontSize: 48 | text-5xl |
fontWeight: "400" | font-normal |
fontWeight: "500" | font-medium |
fontWeight: "600" | font-semibold |
fontWeight: "700" | font-bold |
See tailwind-shadcn-mapping.md for the full quick-reference table including all layout, color, radius, typography, and icon mappings.
Color Mapping (Pencil -> Tailwind)
| Pencil Style | Tailwind Class |
|---|---|
fill bound to primary | bg-primary |
fill bound to background | bg-background |
fill bound to card | bg-card |
textColor bound to foreground | text-foreground |
textColor bound to muted-foreground | text-muted-foreground |
textColor bound to primary-foreground | text-primary-foreground |
strokeColor bound to border | border-border |
Always Do
- Load the
frontend-designskill and apply its aesthetic guidelines to the generated code - Use semantic Tailwind utilities (
bg-primary,text-foreground,rounded-lg) - Map Pencil reusable components to shadcn/ui components where a match exists
- Use CVA for custom components with variants
- Use
cn()from@/lib/utilsfor conditional class merging - Use Lucide icons instead of Pencil's Material Icons (see icon mapping below)
- Use
@theme { --color-* }for color tokens,@theme { --radius-* }for radii - Map
refinstances to component usages with the appropriate variant/size props - Generate TypeScript (not JavaScript)
- Use React 19 patterns (ref as prop, no
forwardRef)
Never Do
- Use arbitrary value syntax:
bg-[#3b82f6],text-[var(--primary)],rounded-[6px] - Use
var(--primary)in className strings - Hardcode hex colors or pixel radii in class names
- Inline all styles when a shadcn/ui component exists
- Ignore the component hierarchy from the design tree
- Generate a single monolithic file for a multi-component screen
- Use
tailwind.config.ts(Tailwind v4 uses CSS@theme) - Use
@tailwind base/components/utilities(v4 uses@import "tailwindcss") - Use
forwardRef(React 19 passes ref as a regular prop) - Skip the
frontend-designskill — it is mandatory for both design and code generation - Produce generic AI aesthetics (overused fonts, cliched color schemes, predictable layouts)
Icon Library Mapping
Pencil uses Material Icons by default. Map them to Lucide icons:
| Pencil Icon (Material) | Lucide Import | Component |
|---|---|---|
search | lucide-react | <Search /> |
close | lucide-react | <X /> |
menu | lucide-react | <Menu /> |
arrow_forward | lucide-react | <ArrowRight /> |
arrow_back | lucide-react | <ArrowLeft /> |
person | lucide-react | <User /> |
settings | lucide-react | <Settings /> |
home | lucide-react | <Home /> |
notifications | lucide-react | <Bell /> |
edit | lucide-react | <Pencil /> |
delete | lucide-react | <Trash2 /> |
add | lucide-react | <Plus /> |
check | lucide-react | <Check /> |
visibility | lucide-react | <Eye /> |
visibility_off | lucide-react | <EyeOff /> |
chevron_right | lucide-react | <ChevronRight /> |
chevron_down | lucide-react | <ChevronDown /> |
more_vert | lucide-react | <MoreVertical /> |
more_horiz | lucide-react | <MoreHorizontal /> |
mail | lucide-react | <Mail /> |
calendar_today | lucide-react | <Calendar /> |
favorite | lucide-react | <Heart /> |
star | lucide-react | <Star /> |
download | lucide-react | <Download /> |
upload | lucide-react | <Upload /> |
filter_list | lucide-react | <Filter /> |
sort | lucide-react | <ArrowUpDown /> |
logout | lucide-react | <LogOut /> |
All Lucide icons accept a className prop for sizing: <Search className="size-4" />.
Utility Setup
Ensure lib/utils.ts exists:
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}Layout and Text Overflow
Why This Matters
Text and content overflowing outside its parent container or the artboard is one of the most common and visible design defects. It produces:
- Unreadable, clipped text
- Broken layouts on mobile screens
- Code that requires manual overflow fixes
- An unprofessional, broken appearance
This is especially critical for mobile designs where the artboard is typically only 375-393px wide.
Prevention Strategy
For Text Elements
1. Always set text width to fill its container:
text=I(container, { type: "text", content: "Long text...", width: "fill_container" })2. Use appropriate text properties:
- Set
maxLinesfor text that should truncate (e.g., card titles, list items) - Long paragraphs should wrap naturally within their container
- Headings that are too long should either wrap or be truncated with ellipsis
3. Never use fixed pixel widths wider than the parent on text elements
For Container Frames
1. Use auto-layout (layout: "vertical" or layout: "horizontal") on parent frames so children flow naturally
2. Constrain children to parent width:
child=I(parent, { type: "frame", width: "fill_container", layout: "vertical" })3. Set padding on parent frames to prevent content from touching edges:
U("parentId", { padding: 16 })
// or per-side: paddingLeft, paddingRight, paddingTop, paddingBottom4. Use `gap` for spacing between children instead of margin hacks:
U("parentId", { layout: "vertical", gap: 12 })For Mobile Screens (375-393px)
Mobile layouts are the most prone to overflow. Extra care required:
1. Screen frame: Set to exactly the target width (e.g., 375px) 2. All direct children: Use width: "fill_container" with horizontal padding (16-20px) 3. Text: Always width: "fill_container", never a fixed width wider than ~335px (375 - 220 padding) 4. Images: Constrain to container width or use `width: "fill_container"` 5. Horizontal scroll areas*: Only use intentionally (e.g., carousels), never by accident
For Nested Components
When inserting a ref component instance:
1. Set the instance width to "fill_container" if it should fill its parent:
card=I(container, { type: "ref", ref: "CardComponent", width: "fill_container" })2. Verify the component's internal layout handles different widths correctly
Detection: Post-Build Verification
After inserting content, always check for overflow:
pencil_snapshot_layout({
filePath: "path/to/file.pen",
parentId: "screenId",
maxDepth: 3,
problemsOnly: true
})This returns only nodes with layout problems:
- Clipped elements: Children extending beyond parent bounds
- Overlapping elements: Siblings overlapping unintentionally
- Overflow: Content wider or taller than its container
Interpreting Results
If problemsOnly returns results, fix each issue:
| Problem | Likely Fix |
|---|---|
| Text clipped horizontally | Set text width: "fill_container" or reduce font size |
| Text clipped vertically | Increase parent height, use auto-height, or set maxLines |
| Child wider than parent | Set child width: "fill_container" instead of fixed width |
| Children overlapping | Add layout: "vertical" or layout: "horizontal" to parent |
| Content outside artboard | Reduce widths/padding, check all descendants fit within screen width |
Fix Patterns
Fix: Text Overflowing Parent
// Before: fixed width wider than parent
U("textNodeId", { width: "fill_container" })Fix: Children Overflowing Frame
// Add auto-layout so children stack instead of overlapping
U("parentFrameId", { layout: "vertical", gap: 8 })
// Make children fill parent width
U("child1Id", { width: "fill_container" })
U("child2Id", { width: "fill_container" })Fix: Content Touching Screen Edges
// Add horizontal padding to the screen's content container
U("contentContainerId", { paddingLeft: 16, paddingRight: 16 })Fix: Long Title Truncation
// Truncate to single line with ellipsis
U("titleTextId", { maxLines: 1, width: "fill_container" })Checklist
After every section of a design:
- [ ] Have I called
pencil_snapshot_layoutwithproblemsOnly: true? - [ ] Are all text elements using
width: "fill_container"inside auto-layout parents? - [ ] Do mobile screens have appropriate padding (16-20px)?
- [ ] Are long titles/descriptions set with
maxLinesfor truncation? - [ ] Do all child frames use
width: "fill_container"(not fixed widths wider than parent)? - [ ] Have I verified the full screen with
pencil_get_screenshot?
See Also
- visual-verification.md — Screenshot verification workflow to catch visual overflow
- responsive-breakpoints.md — Mobile-specific layout constraints and patterns
Responsive Breakpoints
Why This Matters
Pencil designs often use multiple artboards at different widths to represent mobile, tablet, and desktop views. When generating code, these artboard sizes must map to Tailwind CSS breakpoints correctly — otherwise the responsive behavior in the browser won't match the design.
Getting this wrong produces:
- Layouts that break at wrong screen sizes
- Mobile designs appearing at tablet widths
- Desktop layouts that don't scale down properly
- Redundant or missing breakpoint overrides
Pencil Artboard Sizes -> Tailwind Breakpoints
Standard Artboard Widths
| Device | Pencil Artboard Width | Tailwind Breakpoint | Prefix |
|---|---|---|---|
| Mobile (small) | 320px | Default (no prefix) | — |
| Mobile (standard) | 375px | Default (no prefix) | — |
| Mobile (large) | 393-430px | Default (no prefix) | — |
| Tablet (portrait) | 768px | md | md: |
| Tablet (landscape) | 1024px | lg | lg: |
| Desktop | 1280px | xl | xl: |
| Desktop (wide) | 1440px | 2xl | 2xl: |
| Desktop (ultrawide) | 1920px | 2xl or custom | 2xl: |
Tailwind v4 Breakpoint Values
These are Tailwind's default breakpoints (unchanged in v4):
/* Built into Tailwind — no @theme needed */
sm → 640px
md → 768px
lg → 1024px
xl → 1280px
2xl → 1536pxCustom breakpoints in Tailwind v4 use @theme:
@theme {
--breakpoint-xs: 475px;
--breakpoint-3xl: 1920px;
}Mapping Pencil Multi-Artboard Designs to Code
Reading Multiple Artboards
When a design has artboards at different widths, read all of them:
pencil_batch_get({
filePath: "path/to/file.pen",
patterns: [{ type: "frame", name: "Mobile|Tablet|Desktop" }],
readDepth: 4
})Or read top-level nodes to identify all screens:
pencil_batch_get({ filePath: "path/to/file.pen" })Code Generation Strategy
Generate mobile-first code, then add breakpoint overrides for larger screens:
// Mobile-first: base styles match the mobile artboard
// md: styles match the tablet artboard
// lg: styles match the desktop artboard
<div className="flex flex-col gap-4 p-4 md:flex-row md:gap-6 md:p-6 lg:gap-8 lg:p-8">
{/* Sidebar: stacks below content on mobile, beside it on tablet+ */}
<aside className="w-full md:w-64 lg:w-72">
{/* ... */}
</aside>
{/* Main content: full width on mobile, flexible on tablet+ */}
<main className="flex-1">
{/* ... */}
</main>
</div>Common Responsive Patterns
| Pencil Design Pattern | Tailwind Implementation |
|---|---|
| Single column (mobile) -> 2 columns (tablet) -> 3 columns (desktop) | grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 |
| Stacked sidebar (mobile) -> side-by-side (desktop) | flex flex-col lg:flex-row |
| Hidden on mobile, visible on desktop | hidden lg:block |
| Visible on mobile, hidden on desktop | block lg:hidden |
| Full-width mobile, constrained desktop | w-full max-w-7xl mx-auto |
| Small text mobile, larger desktop | text-sm md:text-base lg:text-lg |
| Reduced padding mobile, more desktop | p-4 md:p-6 lg:p-8 |
| Card grid: 1 col mobile, 2 tablet, 3 desktop | grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6 |
| Navigation hamburger (mobile) -> full nav (desktop) | Mobile: <Sheet> / Desktop: <nav className="hidden md:flex"> |
Layout Differences Between Artboards
When comparing mobile vs desktop artboards, look for these differences:
| What Changes | Mobile Artboard | Desktop Artboard | Code Pattern |
|---|---|---|---|
| Layout direction | layout: "vertical" | layout: "horizontal" | flex flex-col lg:flex-row |
| Column count | 1 column | 2-4 columns | grid-cols-1 lg:grid-cols-3 |
| Visibility | Element missing | Element present | hidden lg:block |
| Font size | Smaller | Larger | text-2xl lg:text-4xl |
| Padding | 16px | 24-32px | p-4 lg:p-8 |
| Gap | 16px | 24px | gap-4 lg:gap-6 |
| Sidebar | Hidden or stacked | Side-by-side | hidden lg:block lg:w-64 |
| Image size | Smaller/cropped | Full size | h-48 lg:h-80 |
Container Queries (Advanced)
For component-level responsiveness (where a card adapts based on its container, not the viewport), Tailwind v4 supports container queries:
@theme {
/* No setup needed — Tailwind v4 supports @container natively */
}<div className="@container">
<div className="flex flex-col @md:flex-row @lg:gap-8">
{/* Responds to parent container width, not viewport */}
</div>
</div>Use container queries when the same component appears in different contexts (sidebar vs main content) and should adapt accordingly.
Anti-Patterns
| Wrong | Right |
|---|---|
| Hardcoding pixel widths from artboard | Use Tailwind breakpoints + responsive utilities |
| Building separate components for mobile/desktop | One component with responsive classes |
Using max-width media queries | Mobile-first with min-width (Tailwind default) |
| Ignoring the mobile artboard | Always start from mobile, add md: / lg: overrides |
Using @media in CSS for breakpoints | Use Tailwind responsive prefixes in className |
Generating w-[375px] from mobile artboard | Use w-full with responsive max-width |
Generating w-[1440px] from desktop artboard | Use max-w-7xl mx-auto or similar |
Checklist
When generating code from multi-artboard Pencil designs:
- [ ] Have I identified all artboard sizes and mapped them to Tailwind breakpoints?
- [ ] Am I generating mobile-first code (base styles = mobile artboard)?
- [ ] Am I using breakpoint prefixes (
md:,lg:) for tablet/desktop overrides? - [ ] Have I compared artboards to identify what changes between sizes?
- [ ] Am I using
grid-cols-*with breakpoint prefixes for column count changes? - [ ] Am I using
hidden/blockwith breakpoint prefixes for visibility changes? - [ ] Am I avoiding hardcoded pixel widths from artboard dimensions?
Tailwind + shadcn/ui Mapping Reference
Quick-reference mapping from Pencil design properties to Tailwind v4 + shadcn/ui code. Use this when generating code from Pencil designs.
Color Tokens
Pencil Variable -> Tailwind Utility
| Pencil Variable | Background | Text | Border |
|---|---|---|---|
primary | bg-primary | text-primary | border-primary |
primary-foreground | bg-primary-foreground | text-primary-foreground | — |
secondary | bg-secondary | text-secondary | border-secondary |
secondary-foreground | — | text-secondary-foreground | — |
background | bg-background | — | — |
foreground | — | text-foreground | — |
muted | bg-muted | — | — |
muted-foreground | — | text-muted-foreground | — |
accent | bg-accent | — | — |
accent-foreground | — | text-accent-foreground | — |
destructive | bg-destructive | text-destructive | border-destructive |
destructive-foreground | — | text-destructive-foreground | — |
card | bg-card | — | — |
card-foreground | — | text-card-foreground | — |
border | — | — | border-border |
ring | — | — | ring-ring |
`@theme` rule: All color variables use --color- prefix: --color-primary, --color-border, etc.
Common Combinations
| Design Intent | Tailwind Classes |
|---|---|
| Primary button | bg-primary text-primary-foreground |
| Secondary button | bg-secondary text-secondary-foreground |
| Destructive button | bg-destructive text-destructive-foreground |
| Ghost button | hover:bg-accent hover:text-accent-foreground |
| Outline button | border border-border bg-background hover:bg-accent |
| Card surface | bg-card text-card-foreground border border-border |
| Muted text | text-muted-foreground |
| Page background | bg-background text-foreground |
| Input field | border border-border bg-background text-foreground placeholder:text-muted-foreground |
| Badge | bg-primary text-primary-foreground or bg-secondary text-secondary-foreground |
Radius Tokens
| Pencil Variable | @theme Declaration | Tailwind Utility |
|---|---|---|
radius-sm | --radius-sm: 0.25rem | rounded-sm |
radius-md | --radius-md: 0.375rem | rounded-md |
radius-lg | --radius-lg: 0.5rem | rounded-lg |
radius-xl | --radius-xl: 0.75rem | rounded-xl |
Layout Properties
| Pencil Property | Tailwind Class |
|---|---|
layout: "vertical" | flex flex-col |
layout: "horizontal" | flex or flex flex-row |
gap: 4 | gap-1 |
gap: 8 | gap-2 |
gap: 12 | gap-3 |
gap: 16 | gap-4 |
gap: 20 | gap-5 |
gap: 24 | gap-6 |
gap: 32 | gap-8 |
padding: 8 | p-2 |
padding: 12 | p-3 |
padding: 16 | p-4 |
padding: 20 | p-5 |
padding: 24 | p-6 |
padding: 32 | p-8 |
width: "fill_container" | w-full |
height: "fill_container" | h-full or flex-1 |
alignItems: "center" | items-center |
alignItems: "start" | items-start |
alignItems: "end" | items-end |
justifyContent: "center" | justify-center |
justifyContent: "space-between" | justify-between |
justifyContent: "end" | justify-end |
Typography
| Pencil Property | Tailwind Class |
|---|---|
fontSize: 12 | text-xs |
fontSize: 14 | text-sm |
fontSize: 16 | text-base |
fontSize: 18 | text-lg |
fontSize: 20 | text-xl |
fontSize: 24 | text-2xl |
fontSize: 30 | text-3xl |
fontSize: 36 | text-4xl |
fontSize: 48 | text-5xl |
fontWeight: "400" | font-normal |
fontWeight: "500" | font-medium |
fontWeight: "600" | font-semibold |
fontWeight: "700" | font-bold |
Pencil Component -> shadcn/ui Mapping
| Pencil Component | shadcn/ui | Key Classes |
|---|---|---|
| Button | <Button> | bg-primary text-primary-foreground rounded-md |
| Card | <Card> | rounded-lg border border-border bg-card text-card-foreground shadow-sm |
| Input | <Input> | rounded-md border border-border bg-background |
| Select | <Select> | Uses Radix primitives |
| Badge | <Badge> | rounded-md bg-primary text-primary-foreground |
| Avatar | <Avatar> | rounded-full |
| Separator | <Separator> | bg-border |
| Switch | <Switch> | Uses Radix primitives |
| Checkbox | <Checkbox> | Uses Radix primitives |
| Dialog | <Dialog> | bg-background border border-border rounded-lg shadow-lg |
| Tabs | <Tabs> | bg-muted rounded-md (for TabsList) |
| Table | <Table> | border-border text-foreground |
| Tooltip | <Tooltip> | bg-primary text-primary-foreground rounded-md |
| Label | <Label> | text-sm font-medium |
Icon Mapping (Material -> Lucide)
| Pencil (Material) | Lucide Component | Size Class |
|---|---|---|
search | <Search /> | size-4 or size-5 |
close | <X /> | size-4 |
menu | <Menu /> | size-5 |
arrow_forward | <ArrowRight /> | size-4 |
arrow_back | <ArrowLeft /> | size-4 |
person | <User /> | size-4 |
settings | <Settings /> | size-4 |
home | <Home /> | size-4 or size-5 |
notifications | <Bell /> | size-4 |
edit | <Pencil /> | size-4 |
delete | <Trash2 /> | size-4 |
add | <Plus /> | size-4 |
check | <Check /> | size-4 |
visibility | <Eye /> | size-4 |
visibility_off | <EyeOff /> | size-4 |
chevron_right | <ChevronRight /> | size-4 |
chevron_down | <ChevronDown /> | size-4 |
more_vert | <MoreVertical /> | size-4 |
more_horiz | <MoreHorizontal /> | size-4 |
favorite | <Heart /> | size-4 |
star | <Star /> | size-4 |
download | <Download /> | size-4 |
upload | <Upload /> | size-4 |
filter_list | <Filter /> | size-4 |
sort | <ArrowUpDown /> | size-4 |
mail | <Mail /> | size-4 |
calendar_today | <Calendar /> | size-4 |
logout | <LogOut /> | size-4 |
Anti-Patterns
These patterns indicate a code generation error. If you see them, fix immediately:
WRONG RIGHT
───── ─────
bg-[#3b82f6] bg-primary
text-[#ffffff] text-primary-foreground
text-[var(--primary)] text-primary
bg-[var(--secondary)] bg-secondary
rounded-[6px] rounded-md
rounded-[var(--radius-md)] rounded-md
border-[#e2e8f0] border-border
ring-[var(--ring)] ring-ring
border-[1px] border
opacity-[0.5] opacity-50
gap-[16px] gap-4
p-[24px] p-6
text-[14px] text-smSee Also
- design-to-code-workflow.md — Complete step-by-step code generation workflow
- variables-and-tokens.md — How to read and map Pencil design tokens
- responsive-breakpoints.md — Artboard sizes to Tailwind breakpoints
Variables and Design Tokens
Why This Matters
Pencil variables are the equivalent of design tokens. When you hardcode values like fill: "#3b82f6" or cornerRadius: 8 instead of referencing a variable:
- Code generation produces hardcoded hex values or arbitrary Tailwind classes like
bg-[#3b82f6], making theming impossible - Dark mode won't work because the values don't adapt to theme changes
- Global design updates require manual find-and-replace instead of changing one variable
- The design diverges from the codebase's token system
Step-by-Step: Reading and Using Variables
Step 1: Read All Variables
Always do this at the start of any design task:
pencil_get_variables({ filePath: "path/to/file.pen" })This returns all defined variables with their values, organized by theme. Example output:
{
"variables": {
"primary": { "value": "#3b82f6" },
"primary-foreground": { "value": "#ffffff" },
"secondary": { "value": "#64748b" },
"background": { "value": "#ffffff" },
"foreground": { "value": "#0a0a0a" },
"border": { "value": "#e2e8f0" },
"radius-sm": { "value": 4 },
"radius-md": { "value": 6 },
"radius-lg": { "value": 8 },
"radius-xl": { "value": 12 }
}
}Step 2: Map Your Values to Variables
Before applying any style, check if a variable exists for it:
| What you want | Don't use | Use instead |
|---|---|---|
| Blue brand color | fill: "#3b82f6" | Reference the primary variable |
| White text on primary | textColor: "#ffffff" | Reference the primary-foreground variable |
| Border color | strokeColor: "#e2e8f0" | Reference the border variable |
| Medium rounding | cornerRadius: [6,6,6,6] | Reference the radius-md variable |
| Page background | fill: "#ffffff" | Reference the background variable |
| Body text color | textColor: "#0a0a0a" | Reference the foreground variable |
Step 3: Apply Variables in Design
When the .pen file schema supports variable binding, bind properties to variables instead of using raw values. The exact binding mechanism depends on the schema returned by pencil_get_editor_state - consult the schema for the correct variable reference syntax.
Step 4: Create Missing Variables
If you need a token that doesn't exist, create it:
pencil_set_variables({
filePath: "path/to/file.pen",
variables: {
"accent": { "value": "#f59e0b" },
"accent-foreground": { "value": "#ffffff" }
}
})Then use the new variable instead of hardcoding.
Theme Support
Variables can have different values per theme (e.g., light and dark mode):
{
"primary": {
"themes": {
"light": "#3b82f6",
"dark": "#60a5fa"
}
}
}When using themed variables, the design automatically adapts when switching themes. Hardcoded values break this entirely.
Variables in Code Generation (Tailwind v4 + shadcn/ui)
When generating code from a Pencil design, Pencil variables map to Tailwind v4 semantic utility classes. NEVER use arbitrary value syntax.
Color Token Mapping
Pencil variables map to @theme { --color-* } declarations in your CSS, which Tailwind v4 auto-generates into semantic utility classes:
| Pencil Variable | @theme declaration | Tailwind Utility Classes |
|---|---|---|
primary | --color-primary | bg-primary, text-primary, border-primary |
primary-foreground | --color-primary-foreground | text-primary-foreground, bg-primary-foreground |
secondary | --color-secondary | bg-secondary, text-secondary |
secondary-foreground | --color-secondary-foreground | text-secondary-foreground |
background | --color-background | bg-background |
foreground | --color-foreground | text-foreground |
muted | --color-muted | bg-muted, text-muted |
muted-foreground | --color-muted-foreground | text-muted-foreground |
accent | --color-accent | bg-accent |
accent-foreground | --color-accent-foreground | text-accent-foreground |
destructive | --color-destructive | bg-destructive, text-destructive |
destructive-foreground | --color-destructive-foreground | text-destructive-foreground |
card | --color-card | bg-card |
card-foreground | --color-card-foreground | text-card-foreground |
border | --color-border | border-border |
ring | --color-ring | ring-ring |
Radius Token Mapping
Pencil radius variables map to @theme { --radius-* } declarations, which generate rounded-* utilities:
| Pencil Variable | @theme declaration | Tailwind Utility |
|---|---|---|
radius-sm | --radius-sm | rounded-sm |
radius-md | --radius-md | rounded-md |
radius-lg | --radius-lg | rounded-lg |
radius-xl | --radius-xl | rounded-xl |
What NEVER to Generate
| Bad (arbitrary values) | Good (semantic utilities) |
|---|---|
bg-[#3b82f6] | bg-primary |
text-[#ffffff] | text-primary-foreground |
text-[var(--primary)] | text-primary |
bg-[var(--secondary)] | bg-secondary |
rounded-[6px] | rounded-md |
rounded-[var(--radius-md)] | rounded-md |
border-[#e2e8f0] | border-border |
ring-[var(--ring)] | ring-ring |
The rule is simple: if a Pencil variable exists for the value, there is a corresponding semantic Tailwind utility. Use the utility, not arbitrary syntax.
Opacity Modifiers
Tailwind v4 supports opacity modifiers on semantic classes:
bg-primary/90 -> primary color at 90% opacity
text-foreground/70 -> foreground color at 70% opacity
border-border/50 -> border color at 50% opacityUse these instead of arbitrary opacity values or color-mix() in class names.
Checklist
Before applying any style value:
- [ ] Have I called
pencil_get_variablesto see available tokens? - [ ] Am I using a variable reference instead of a hardcoded color value?
- [ ] Am I using a variable reference instead of a hardcoded border radius?
- [ ] If the needed variable doesn't exist, have I created it with
pencil_set_variables? - [ ] For code generation: am I outputting semantic Tailwind classes (
bg-primary,rounded-md), NOT arbitrary values (bg-[#3b82f6],rounded-[6px])?
Common Variable Categories
| Category | Common Variable Names | Tailwind Prefix |
|---|---|---|
| Brand colors | primary, secondary, accent | --color-* -> bg-*, text-* |
| Semantic colors | destructive, success, warning, info | --color-* -> bg-*, text-* |
| Surface colors | background, foreground, card, card-foreground | --color-* -> bg-*, text-* |
| UI colors | border, ring, muted, muted-foreground | --color-* -> border-*, ring-* |
| Border radius | radius-sm, radius-md, radius-lg, radius-xl | --radius-* -> rounded-* |
| Typography | font-sans, font-mono, font-heading | --font-* -> font-* |
| Spacing | spacing-xs, spacing-sm, spacing-md, spacing-lg | --spacing-* -> gap-*, p-* |
See Also
- tailwind-shadcn-mapping.md — Full quick-reference mapping tables for code generation
- design-to-code-workflow.md — Complete code generation workflow using these tokens
- responsive-breakpoints.md — Breakpoint tokens and responsive patterns
Visual Verification
Why This Matters
Layout and spacing issues are invisible in the node tree. The only way to catch them is to look at the rendered output. Common issues that only screenshots reveal:
- Misaligned elements that seem correct in the tree
- Spacing that is technically valid but visually unbalanced
- Text that renders differently than expected (wrong font size, weight, color contrast)
- Colors that look wrong together despite individual correctness
- Missing content or empty areas
- Broken component instances
Verification Workflow
Section-by-Section Verification
Do NOT build an entire screen and then verify at the end. Verify after each logical section:
Build header -> Screenshot header -> Fix issues
Build hero -> Screenshot hero -> Fix issues
Build features -> Screenshot features -> Fix issues
Build footer -> Screenshot footer -> Fix issues
Final -> Screenshot full page -> Final reviewStep 1: Take a Screenshot
After completing a section:
pencil_get_screenshot({
filePath: "path/to/file.pen",
nodeId: "sectionNodeId"
})For the full screen at the end:
pencil_get_screenshot({
filePath: "path/to/file.pen",
nodeId: "screenNodeId"
})Step 2: Analyze the Screenshot
Look for these specific issues:
Alignment
- Are elements properly centered or left/right aligned?
- Do columns have equal widths?
- Are grid items consistently spaced?
Spacing
- Is there adequate padding inside containers?
- Are gaps between elements consistent?
- Does the spacing follow an 8px grid or the design system's spacing scale?
Typography
- Is text readable (sufficient size and contrast)?
- Are headings visually distinct from body text?
- Is any text cut off, overlapping, or overflowing?
Color and Contrast
- Do colors match the design system variables?
- Is there sufficient contrast for readability?
- Does the color scheme feel cohesive?
Completeness
- Are all expected elements present?
- Are icons/images placed correctly?
- Are there any empty or broken areas?
Step 3: Check Layout Problems
In parallel with the screenshot, run layout checks:
pencil_snapshot_layout({
filePath: "path/to/file.pen",
parentId: "sectionNodeId",
maxDepth: 3,
problemsOnly: true
})This catches programmatic issues the screenshot might not make obvious:
- Clipped elements (content extends beyond parent bounds)
- Overlapping siblings
- Elements positioned outside their parent
Step 4: Fix and Re-verify
If issues are found:
1. Fix each issue using pencil_batch_design with U() operations 2. Take a new screenshot to confirm the fix 3. Run pencil_snapshot_layout again to confirm no new issues
When to Take Screenshots
| Moment | What to Screenshot |
|---|---|
| After building a section | The section's root frame |
| After fixing an issue | The affected area |
| When design is complete | The full screen/artboard |
| After modifying an existing design | The changed section |
| After bulk property updates | At least one affected area |
| When comparing variants | Both variants side by side |
Full-Screen Final Review Checklist
When taking the final full-screen screenshot:
- [ ] Does the overall visual hierarchy make sense?
- [ ] Are sections clearly separated?
- [ ] Is the spacing consistent from top to bottom?
- [ ] Does it look professional and polished?
- [ ] On mobile: does everything fit within the 375px frame?
- [ ] Are there any empty gaps or broken areas?
- [ ] Does the color scheme work as a whole?
- [ ] Is there adequate white space?
Common Issues Found Only Through Screenshots
| Issue | Symptom in Screenshot |
|---|---|
| Wrong font weight | Text looks too thin or too bold compared to surroundings |
| Inconsistent padding | Some cards have more internal space than others |
| Color too similar to background | Element is hard to see or "disappears" |
| Alignment drift | Elements seem slightly off from each other |
| Missing gap | Two sections run directly into each other |
| Broken auto-layout | Children stack in unexpected direction |
| Icon too small/large | Icon is disproportionate to adjacent text |
| Image aspect ratio wrong | Image appears stretched or squished |
See Also
- layout-and-text-overflow.md — Fix patterns for common overflow issues
- asset-reuse.md — Verify copied assets look correct after placement
Related skills
Forks & variants (2)
Pencil Design has 2 known copies in the catalog totaling 129 installs. They canonicalize to this original listing.
- chiroro-jr - 128 installs
- seed-hypermedia - 1 installs
How it compares
Choose pencil-design when the source of truth is a Pencil .pen file and you need verified multi-framework code export rather than one-off mockups.
FAQ
Who is Pencil Design for?
Developers and software engineers working with pencil-design patterns from the skill documentation.
When should I use Pencil Design?
Design UIs in Pencil (.pen files) and generate production code from them. Use when working with .pen files, designing screens or components in Pencil, or generating code from Pencil designs. Triggers on tasks involving P
Is Pencil Design safe to install?
Review the Security Audits panel on this page before installing in production.