Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    nickbreaton

    pencil

    nickbreaton/pencil
    Design
    2 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Design UI in .pen files using Pencil MCP (pencil.dev). Use this skill when creating designs, screens, dashboards, landing pages, components, or editing .pen files...

    SKILL.md

    Pencil MCP Design Skill

    This skill provides comprehensive guidance for designing user interfaces using the Pencil MCP tools. Pencil uses .pen files — a JSON-based design format with flexbox layout, components, variables, and theming support.


    Quick Reference: Essential Workflow

    1. pencil_get_editor_state       → Get current file, selection, schema
    2. pencil_get_guidelines         → Load topic-specific rules (design-system, landing-page, table)
    3. pencil_get_style_guide_tags   → Get available style tags (if designing from scratch)
    4. pencil_get_style_guide        → Get visual direction with 5-10 tags
    5. pencil_get_variables          → Read design tokens ($--colors, $--fonts, etc.)
    6. pencil_batch_get              → Inspect existing nodes and components
    7. pencil_batch_design           → Create/modify design (max 25 ops per call)
    8. pencil_get_screenshot         → Verify visual output
    

    Core Concepts

    The .pen File Structure

    A .pen file is a JSON document containing:

    • children: Array of top-level frames (screens, components)
    • variables: Design tokens (colors, fonts, radii)
    • themes: Theme axes and values
    • fonts: Custom font definitions

    Node Types

    Type Description Key Properties
    frame Container with layout layout, gap, padding, fill, clip
    text Text content content, fontSize, fontFamily, fill, textGrowth
    rectangle Shape fill, stroke, cornerRadius
    ellipse Oval/circle fill, stroke
    icon_font Icon from font set iconFontFamily, iconFontName, fill
    ref Component instance ref (points to reusable component), descendants
    group Logical grouping children, optional layout

    Flexbox Layout System

    Frames use flexbox by default. Key properties:

    {
      layout: "vertical" | "horizontal" | "none",  // none = absolute positioning
      gap: 16,                                      // spacing between children
      padding: 24 | [24, 32] | [top, right, bottom, left],
      justifyContent: "start" | "center" | "end" | "space_between" | "space_around",
      alignItems: "start" | "center" | "end"
    }
    

    Dynamic Sizing

    Value Behavior
    "fill_container" Expand to fill parent (requires parent layout)
    "fit_content" Shrink to content size
    "fill_container(200)" Fill with 200px fallback
    "fit_content(200)" Fit content with 200px fallback
    200 Fixed 200px

    Critical Rules:

    • fill_container only works when parent has layout: "vertical" or "horizontal"
    • fit_content only works on frames with layout
    • Cannot have all children as fill_container while parent is fit_content (circular dependency)

    batch_design Operations

    The primary design tool. Supports Insert, Copy, Update, Replace, Move, Delete, and Generate operations.

    Operation Syntax

    // Insert - create new node
    binding=I(parent, {type: "frame", ...props})
    
    // Copy - duplicate existing node
    binding=C("sourceId", parent, {descendants: {...overrides}})
    
    // Update - modify properties (NOT children)
    U("nodeId", {property: "value"})
    U(binding+"/childId", {content: "Updated"})
    
    // Replace - swap node entirely
    binding=R("nodeId", {type: "text", content: "New content"})
    
    // Move - change parent or order
    M("nodeId", "newParent", index)
    
    // Delete - remove node
    D("nodeId")
    
    // Generate image - apply to frame/rectangle
    G(binding, "ai" | "stock", "prompt describing image")
    

    Critical Rules

    1. Maximum 25 operations per call — split larger designs into multiple calls
    2. Bindings are single-use — each Insert/Copy/Replace creates a binding valid only within that call
    3. IDs regenerate on Copy — use descendants in Copy operation, NOT separate Update calls:
    // CORRECT - override during copy
    copiedBtn=C("btnId", container, {descendants: {"labelId": {content: "New Text"}}})
    
    // WRONG - IDs changed, will fail
    copiedBtn=C("btnId", container, {})
    U(copiedBtn+"/labelId", {content: "New Text"})  // Error: node not found
    
    1. Use placeholders for screens — always set placeholder: true while working, remove when done:
    // Start work
    screen=I(document, {type: "frame", name: "Dashboard", placeholder: true, ...})
    
    // ... do design work ...
    
    // Finish
    U("screenId", {placeholder: false})
    
    1. Never set x/y in flexbox — position properties are ignored when parent has layout

    Working with Component Instances

    Components are nodes with reusable: true. Instances use type: "ref":

    // Insert instance
    card=I(container, {type: "ref", ref: "CardComponentId"})
    
    // Override instance properties (root level)
    card=I(container, {type: "ref", ref: "CardId", width: "fill_container"})
    
    // Override descendant properties
    U(card+"/titleText", {content: "New Title"})
    U(card+"/icon", {iconFontName: "settings"})
    
    // Replace slot content entirely
    newContent=R(card+"/contentSlot", {type: "frame", layout: "vertical", children: [...]})
    
    // Insert into slots
    item=I(card+"/slotId", {type: "ref", ref: "ListItemId"})
    

    Nested Instance Overrides

    For deeply nested components, use path notation:

    // sidebar contains menuComponent, which contains buttonComponent
    U("sidebar/menuComponent/buttonComponent/label", {content: "Updated"})
    

    Guidelines Topics

    Call pencil_get_guidelines with specific topics:

    Topic When to Use
    design-system Building with existing components, dashboards, SaaS apps
    landing-page Marketing pages, websites, promotional content
    table Data tables, grids
    tailwind Generating Tailwind CSS code from designs
    code Generating any code from .pen files

    Always load relevant guidelines before starting design work.


    Style Guides

    For creative direction on new designs:

    // 1. Get available tags
    pencil_get_style_guide_tags()
    
    // 2. Select 5-10 relevant tags
    pencil_get_style_guide({
      tags: ["webapp", "dark-mode", "minimal", "professional", "tech"]
    })
    

    Style guides provide:

    • Color palettes with hex values
    • Typography scales (fonts, sizes, weights)
    • Spacing systems (gaps, padding)
    • Component patterns with exact properties
    • Design philosophy and dos/don'ts

    When to use style guides:

    • Designing from scratch (blank canvas)
    • User requests specific aesthetic
    • Landing pages, marketing sites
    • Exploring visual directions

    When to skip:

    • Pure compositional tasks ("add a button here")
    • Using existing design system components

    Text Handling

    textGrowth Property

    Controls text box sizing and wrapping:

    Value Width Height Line Wrap
    "auto" (default) Calculated Calculated Never
    "fixed-width" Must specify Calculated Yes
    "fixed-width-height" Must specify Must specify Yes

    Critical: Never set width/height on text without also setting textGrowth.

    // Single line, auto-sized
    {type: "text", content: "Hello"}
    
    // Multi-line with wrapping
    {type: "text", content: "Long paragraph...", textGrowth: "fixed-width", width: "fill_container"}
    
    // Fixed box with overflow
    {type: "text", content: "Fixed area", textGrowth: "fixed-width-height", width: 200, height: 100}
    

    Text Alignment

    • textAlign: "left" | "center" | "right" | "justify" — horizontal alignment within text box
    • textAlignVertical: "top" | "middle" | "bottom" — vertical alignment

    Note: These only have visible effect with textGrowth set. To position the text box itself, use parent flexbox properties.


    Icons

    Use icon_font type with these font families:

    Family Style Example Names
    lucide Outline, rounded home, settings, user, search, plus, x
    feather Outline, rounded Same as lucide
    Material Symbols Outlined Outline home, settings, person, search, add, close
    Material Symbols Rounded Rounded Same as outlined
    Material Symbols Sharp Sharp corners Same as outlined
    icon=I(container, {
      type: "icon_font",
      iconFontFamily: "lucide",
      iconFontName: "settings",
      width: 24,
      height: 24,
      fill: "#333333"
    })
    

    Must specify both width and height for icons.


    Images

    There is NO "image" node type! Images are fills applied to frame/rectangle nodes.

    Using the G (Generate) Operation

    // Create frame, then apply image
    heroFrame=I(container, {type: "frame", width: 400, height: 300})
    G(heroFrame, "ai", "modern office workspace, bright natural lighting")
    
    // Stock photo
    G("existingFrameId", "stock", "mountain landscape sunset")
    

    Image Types

    Type Source Best For
    "ai" AI-generated Custom illustrations, specific scenes, brand assets
    "stock" Unsplash photos Real photography, authentic imagery

    Writing Effective Prompts

    AI prompts — describe scene, style, mood:

    • Weak: "A laptop"
    • Better: "Modern laptop on wooden desk, soft morning light, minimal workspace"

    Stock queries — use descriptive keywords:

    • "modern office workspace bright"
    • "team collaboration meeting diverse"
    • "abstract gradient blue purple"

    Design Variables

    Use pencil_get_variables to read tokens, then reference with $ prefix:

    {
      type: "text",
      content: "Hello",
      fill: "$--foreground",           // Color variable
      fontFamily: "$--font-primary"    // Font variable
    }
    

    Common variable patterns:

    • $--background, $--foreground — base colors
    • $--primary, $--secondary — brand colors
    • $--font-primary, $--font-secondary — typefaces
    • $--radius-m, $--radius-pill — corner radii
    • $--border — border color

    Always use variables over hardcoded values when available.


    Visual Verification

    Always screenshot after significant changes:

    pencil_get_screenshot({filePath: "design.pen", nodeId: "screenId"})
    

    Check for:

    • Correct spacing and alignment
    • Text overflow or clipping
    • Color contrast issues
    • Missing content
    • Layout problems

    Use pencil_snapshot_layout to check spatial relationships:

    pencil_snapshot_layout({
      filePath: "design.pen",
      parentId: "screenId",
      maxDepth: 2
    })
    

    Returns positions, sizes, and layout problems (clipping, overlaps).


    Common Patterns

    Screen with Sidebar

    screen=I(document, {type: "frame", name: "Dashboard", layout: "horizontal", width: 1440, height: "fit_content(900)", fill: "$--background", placeholder: true})
    sidebar=I(screen, {type: "ref", ref: "sidebarId", height: "fill_container"})
    main=I(screen, {type: "frame", layout: "vertical", width: "fill_container", height: "fill_container", padding: 32, gap: 24})
    

    Card with Header/Content/Actions

    card=I(container, {type: "ref", ref: "cardId", width: "fill_container"})
    header=R(card+"/headerSlot", {type: "frame", layout: "vertical", gap: 4, padding: 24, width: "fill_container", children: [
      {type: "text", content: "Title", fontSize: 18, fontWeight: "600"},
      {type: "text", content: "Description", fontSize: 14, fill: "$--muted-foreground"}
    ]})
    U(card+"/contentSlot", {layout: "vertical", gap: 16, padding: 24})
    U(card+"/actionsSlot", {justifyContent: "end", padding: 24})
    

    Table Structure

    Tables follow strict hierarchy: Table → Row → Cell (frame) → Content

    tableRow=I("tableId", {type: "frame", layout: "horizontal", width: "fill_container"})
    cell1=I(tableRow, {type: "frame", width: "fill_container"})
    cellContent1=I(cell1, {type: "text", content: "John Doe"})
    cell2=I(tableRow, {type: "frame", width: "fill_container"})
    cellContent2=I(cell2, {type: "text", content: "john@example.com"})
    

    Never skip the cell frame — content goes inside cells, not directly in rows.

    Form Layout

    form=I(card+"/contentSlot", {type: "frame", layout: "vertical", gap: 16, width: "fill_container"})
    row=I(form, {type: "frame", layout: "horizontal", gap: 16, width: "fill_container"})
    firstName=I(row, {type: "ref", ref: "inputGroupId", width: "fill_container", descendants: {"labelId": {content: "First Name"}}})
    lastName=I(row, {type: "ref", ref: "inputGroupId", width: "fill_container", descendants: {"labelId": {content: "Last Name"}}})
    email=I(form, {type: "ref", ref: "inputGroupId", width: "fill_container", descendants: {"labelId": {content: "Email"}}})
    

    Metric Cards Grid

    metrics=I(content, {type: "frame", layout: "horizontal", gap: 16, width: "fill_container"})
    metric1=I(metrics, {type: "ref", ref: "metricCardId", width: "fill_container"})
    U(metric1+"/label", {content: "total_users"})
    U(metric1+"/value", {content: "12,543"})
    U(metric1+"/change", {content: "+12.5%"})
    

    Inspection Tools

    pencil_batch_get

    Read node structure:

    // Read specific nodes
    pencil_batch_get({
      filePath: "design.pen",
      nodeIds: ["nodeId1", "nodeId2"],
      readDepth: 3
    })
    
    // Search for patterns
    pencil_batch_get({
      filePath: "design.pen",
      patterns: [{reusable: true}],  // Find all components
      readDepth: 2,
      searchDepth: 5
    })
    
    // Read document root
    pencil_batch_get({
      filePath: "design.pen"
    })
    

    pencil_get_editor_state

    Get current context:

    pencil_get_editor_state({include_schema: true})
    

    Returns:

    • Active file path
    • Selected nodes
    • Reusable components list
    • .pen schema (if requested)

    Error Handling

    Common Errors

    Error Cause Solution
    "Node not found" Wrong ID or ID changed after copy Use descendants in Copy, verify IDs with batch_get
    "oldString found multiple times" Ambiguous match Add more context to oldString
    "Circular dependency" Parent fit_content with all children fill_container Give at least one child fixed size
    "Operations rolled back" Any operation failed Fix the failed operation, re-run entire batch

    Debugging Tips

    1. Use batch_get before modifying — understand current structure
    2. Screenshot frequently — catch visual issues early
    3. Work in small batches — easier to identify failures
    4. Check layout problems — use snapshot_layout with problemsOnly: true

    Anti-Patterns

    Don't

    • Use Update on copied node descendants (IDs change)
    • Set x/y when parent has layout
    • Skip cell frames in tables
    • Hardcode colors when variables exist
    • Create image nodes (use G operation on frames)
    • Set width/height on text without textGrowth
    • Forget placeholder: true when working
    • Forget to remove placeholder when done
    • Exceed 25 operations per batch_design call

    Do

    • Use descendants in Copy for overrides
    • Use fill_container/fit_content for sizing
    • Follow Table → Row → Cell → Content hierarchy
    • Reference $--variables for colors and fonts
    • Apply images as fills via G operation
    • Set textGrowth before width/height
    • Screenshot after every major change
    • Remove placeholders when sections complete
    • Split large designs into logical batches

    Workflow Example: Dashboard Screen

    // 1. Get context
    pencil_get_editor_state({include_schema: false})
    pencil_get_guidelines({topic: "design-system"})
    pencil_get_variables({filePath: "app.pen"})
    
    // 2. Inspect available components
    pencil_batch_get({
      filePath: "app.pen",
      patterns: [{reusable: true}],
      readDepth: 2
    })
    
    // 3. Create screen structure (first batch)
    screen=I(document, {type: "frame", name: "Dashboard", layout: "horizontal", width: 1440, height: "fit_content(900)", fill: "$--background", placeholder: true})
    sidebar=I(screen, {type: "ref", ref: "sidebarId", height: "fill_container"})
    main=I(screen, {type: "frame", layout: "vertical", width: "fill_container", height: "fill_container", padding: 32, gap: 24})
    
    // 4. Add header section (second batch)
    header=I("mainId", {type: "frame", layout: "horizontal", justifyContent: "space_between", alignItems: "center", width: "fill_container"})
    title=I(header, {type: "text", content: "Dashboard", fontSize: 32, fontWeight: "600"})
    actions=I(header, {type: "frame", layout: "horizontal", gap: 12})
    btn=I(actions, {type: "ref", ref: "buttonPrimaryId", descendants: {"labelId": {content: "New Item"}}})
    
    // 5. Add metrics row (third batch)
    metrics=I("mainId", {type: "frame", layout: "horizontal", gap: 16, width: "fill_container"})
    metric1=I(metrics, {type: "ref", ref: "metricCardId", width: "fill_container"})
    metric2=I(metrics, {type: "ref", ref: "metricCardId", width: "fill_container"})
    metric3=I(metrics, {type: "ref", ref: "metricCardId", width: "fill_container"})
    
    // 6. Verify
    pencil_get_screenshot({filePath: "app.pen", nodeId: "screenId"})
    
    // 7. Remove placeholder when done
    U("screenId", {placeholder: false})
    

    Tool Reference Summary

    Tool Purpose Key Parameters
    pencil_get_editor_state Get current file, selection, schema include_schema
    pencil_get_guidelines Load design rules topic
    pencil_get_style_guide_tags List available style tags —
    pencil_get_style_guide Get visual direction tags[] or id
    pencil_get_variables Read design tokens filePath
    pencil_batch_get Read node structure nodeIds, patterns, readDepth, searchDepth
    pencil_batch_design Create/modify design filePath, operations
    pencil_get_screenshot Visual verification filePath, nodeId
    pencil_snapshot_layout Check spatial layout filePath, parentId, maxDepth, problemsOnly
    pencil_find_empty_space_around_node Find placement space nodeId, direction, width, height, padding
    pencil_set_variables Update design tokens filePath, variables
    pencil_open_document Open/create .pen file filePathOrTemplate

    This skill covers the Pencil MCP design system. For authoritative documentation, consult pencil.dev or use pencil_get_guidelines with specific topics.

    Recommended Servers
    MCP Hive
    MCP Hive
    Webflow
    Webflow
    tldraw
    tldraw
    Repository
    nickbreaton/.opencode
    Files