Skill: jcwen/pencil-renderer (version 0.1.2)

--- name: pencil-renderer description: | Render DNA codes to Pencil .pen frames. Does ONE thing well. Input: DNA code + component type (hero, card, form, etc.) Output: .pen frame ID + screenshot Use when: design-exploration or other orchestrators need to render visual proposals using Pencil MCP backend. effort: high --- # Pencil Renderer Translate aesthetic DNA codes into Pencil .pen frames via MCP. ## Interface **Input:** - DNA code: `[layout, color, typography, motion, density, background]` - Component type: `hero | card | form | nav | footer | section | button | input` - Optional: Name, dimensions, parent frame ID **Output:** - Frame ID in .pen file - Screenshot of rendered frame ## Workflow ### 1. Ensure Document Ready ```javascript // Check if editor open mcp__pencil__get_editor_state({ include_schema: false }) // If no editor, open or create mcp__pencil__open_document({ filePathOrTemplate: "new" }) ``` ### 2. Get Style Foundation ```javascript // Get available style guide tags mcp__pencil__get_style_guide_tags() // Get style guide matching DNA mood // Map DNA to relevant tags: // - dark color → ["dark-mode", "moody"] // - light color → ["light", "clean"] // - spacious density → ["airy", "whitespace"] // etc. mcp__pencil__get_style_guide({ tags: [mapped_tags] }) ``` ### 3. Translate DNA to Pencil Properties Reference: `references/dna-to-pencil.md` DNA axis → Pencil property mapping is deterministic. Example: `centered` layout → `alignItems: "center"`, symmetric padding ### 4. Execute Design Operations Reference: `references/batch-design-patterns.md` ```javascript mcp__pencil__batch_design({ filePath: "<path>.pen", operations: ` frame=I(document, {type: "frame", name: "Hero-Brutalist", ...properties}) heading=I(frame, {type: "text", content: "Headline", ...typography}) // ... additional operations ` }) ``` ### 5. Capture Result ```javascript // Screenshot for visual verification mcp__pencil__get_screenshot({ nodeId: "frameId" }) ``` ### 6. Return ```markdown Frame ID: [id] DNA: [layout, color, typography, motion, density, background] ``` Plus screenshot image. ## Component Templates | Type | Structure | |------|-----------| | `hero` | Container + headline + subhead + CTA buttons | | `card` | Container + image area + content + actions | | `form` | Container + labels + inputs + submit | | `nav` | Container + logo + links + actions | | `footer` | Container + columns + links + legal | | `section` | Container + heading + content grid | | `button` | Frame + text + icon slot | | `input` | Frame + label + field + validation | ## DNA Translation Quick Reference | DNA Axis | Key Pencil Properties | |----------|----------------------| | Layout: centered | `alignItems: "center"`, equal padding | | Layout: asymmetric | Offset positions, varied gaps | | Layout: bento | Grid with varied spans | | Color: dark | Dark fill, light foreground | | Color: gradient | `fill: {type: "linear", stops: [...]}` | | Typography: display-heavy | Large heading sizes, high contrast | | Typography: minimal | Restrained scale, single family | | Density: spacious | gap: 24-48, generous padding | | Density: compact | gap: 8-16, tight padding | | Background: solid | Single fill color | | Background: textured | G() for patterns/images | ## Constraints - **Single concern**: Render DNA → frame. No interview, no iteration. - **Pencil MCP required**: Fails fast if unavailable - **Deterministic mapping**: Same DNA always produces same structure - **Composable**: Called by orchestrators, not users directly ## References - `references/dna-to-pencil.md` — Complete axis mapping - `references/batch-design-patterns.md` — Common operation sequences - `aesthetic-system/references/dna-codes.md` — DNA axis definitions ## Integration **Called by:** - `design-exploration` orchestrator (when Pencil backend available) **Composes:** - `aesthetic-system` (for DNA interpretation)

path: references/batch-design-patterns.md
content: # Batch Design Patterns

Common operation sequences for Pencil MCP `batch_design` tool.

## Operation Syntax Quick Reference

```javascript
// Insert node with binding
binding=I("parentId", {type: "frame", ...props})

// Copy node (creates ref to reusable, or duplicate)
copy=C("sourceId", "parentId", {positionDirection: "right"})

// Update existing node properties
U("nodeId", {fill: "#000"})

// Update nested node in component instance
U("instanceId/childId", {content: "New text"})

// Replace node entirely
newNode=R("path/to/node", {type: "text", content: "Replaced"})

// Move node to new parent
M("nodeId", "newParentId", 0)

// Delete node
D("nodeId")

// Generate/get image for frame
G("frameId", "ai", "prompt describing image")
G("frameId", "stock", "search keywords")
```

## Hero Section Pattern

```javascript
// Hero container
hero=I(document, {
  type: "frame",
  name: "Hero",
  width: 1440,
  height: 800,
  layout: "vertical",
  alignItems: "center",
  padding: [120, 80, 120, 80],
  gap: 32,
  fill: "#0A0A0A"
})

// Headline
headline=I(hero, {
  type: "text",
  content: "Hero Headline",
  fontSize: 64,
  fontWeight: "700",
  textColor: "#FAFAFA",
  textAlign: "center"
})

// Subheadline
subhead=I(hero, {
  type: "text",
  content: "Supporting description text",
  fontSize: 20,
  fontWeight: "400",
  textColor: "#A0A0A0",
  textAlign: "center",
  width: 600
})

// CTA container
ctas=I(hero, {
  type: "frame",
  layout: "horizontal",
  gap: 16
})

// Primary button
primary=I(ctas, {
  type: "frame",
  layout: "horizontal",
  alignItems: "center",
  padding: [16, 32, 16, 32],
  cornerRadius: [8, 8, 8, 8],
  fill: "#FFFFFF"
})
primaryText=I(primary, {
  type: "text",
  content: "Get Started",
  fontSize: 16,
  fontWeight: "600",
  textColor: "#0A0A0A"
})

// Secondary button
secondary=I(ctas, {
  type: "frame",
  layout: "horizontal",
  alignItems: "center",
  padding: [16, 32, 16, 32],
  cornerRadius: [8, 8, 8, 8],
  fill: "transparent",
  stroke: "#FFFFFF",
  strokeThickness: 1
})
secondaryText=I(secondary, {
  type: "text",
  content: "Learn More",
  fontSize: 16,
  fontWeight: "500",
  textColor: "#FFFFFF"
})
```

## Card Pattern

```javascript
// Card container
card=I(parent, {
  type: "frame",
  name: "Card",
  width: 360,
  height: 420,
  layout: "vertical",
  gap: 0,
  cornerRadius: [12, 12, 12, 12],
  fill: "#FFFFFF",
  stroke: "#E5E5E5",
  strokeThickness: 1
})

// Image area
imageArea=I(card, {
  type: "frame",
  width: "fill_container",
  height: 200,
  fill: "#F5F5F5"
})
G(imageArea, "stock", "abstract gradient background")

// Content area
content=I(card, {
  type: "frame",
  layout: "vertical",
  padding: [24, 24, 24, 24],
  gap: 12,
  width: "fill_container"
})

// Title
title=I(content, {
  type: "text",
  content: "Card Title",
  fontSize: 20,
  fontWeight: "600",
  textColor: "#1A1A1A"
})

// Description
desc=I(content, {
  type: "text",
  content: "Card description text goes here with more details.",
  fontSize: 14,
  fontWeight: "400",
  textColor: "#6B7280",
  lineHeight: 1.5
})

// Actions
actions=I(card, {
  type: "frame",
  layout: "horizontal",
  padding: [16, 24, 24, 24],
  gap: 12
})
```

## Form Pattern

```javascript
// Form container
form=I(parent, {
  type: "frame",
  name: "Form",
  width: 400,
  layout: "vertical",
  padding: [32, 32, 32, 32],
  gap: 24,
  cornerRadius: [16, 16, 16, 16],
  fill: "#FFFFFF"
})

// Form title
formTitle=I(form, {
  type: "text",
  content: "Sign Up",
  fontSize: 28,
  fontWeight: "700",
  textColor: "#1A1A1A"
})

// Input group factory (repeat for each field)
field1=I(form, {
  type: "frame",
  layout: "vertical",
  gap: 8,
  width: "fill_container"
})
label1=I(field1, {
  type: "text",
  content: "Email",
  fontSize: 14,
  fontWeight: "500",
  textColor: "#374151"
})
input1=I(field1, {
  type: "frame",
  width: "fill_container",
  height: 48,
  padding: [12, 16, 12, 16],
  cornerRadius: [8, 8, 8, 8],
  fill: "#F9FAFB",
  stroke: "#D1D5DB",
  strokeThickness: 1
})
placeholder1=I(input1, {
  type: "text",
  content: "[REDACTED_SECRET]",
  fontSize: 16,
  textColor: "#9CA3AF"
})

// Submit button
submit=I(form, {
  type: "frame",
  width: "fill_container",
  height: 48,
  layout: "horizontal",
  alignItems: "center",
  justifyContent: "center",
  cornerRadius: [8, 8, 8, 8],
  fill: "#2563EB"
})
submitText=I(submit, {
  type: "text",
  content: "Continue",
  fontSize: 16,
  fontWeight: "600",
  textColor: "#FFFFFF"
})
```

## Navigation Pattern

```javascript
// Nav container
nav=I(document, {
  type: "frame",
  name: "Navigation",
  width: "fill_container",
  height: 72,
  layout: "horizontal",
  alignItems: "center",
  justifyContent: "space-between",
  padding: [0, 48, 0, 48],
  fill: "#FFFFFF"
})

// Logo area
logo=I(nav, {
  type: "frame",
  layout: "horizontal",
  alignItems: "center",
  gap: 8
})
logoText=I(logo, {
  type: "text",
  content: "Brand",
  fontSize: 20,
  fontWeight: "700",
  textColor: "#1A1A1A"
})

// Nav links
links=I(nav, {
  type: "frame",
  layout: "horizontal",
  gap: 32
})
link1=I(links, {type: "text", content: "Features", fontSize: 15, textColor: "#4B5563"})
link2=I(links, {type: "text", content: "Pricing", fontSize: 15, textColor: "#4B5563"})
link3=I(links, {type: "text", content: "About", fontSize: 15, textColor: "#4B5563"})

// CTA
navCta=I(nav, {
  type: "frame",
  padding: [10, 20, 10, 20],
  cornerRadius: [6, 6, 6, 6],
  fill: "#1A1A1A"
})
navCtaText=I(navCta, {
  type: "text",
  content: "Sign Up",
  fontSize: 14,
  fontWeight: "500",
  textColor: "#FFFFFF"
})
```

## Bento Grid Pattern

```javascript
// Bento container
bento=I(document, {
  type: "frame",
  name: "Bento Grid",
  width: 1200,
  layout: "grid",
  gap: 16,
  padding: [48, 48, 48, 48]
})

// Large feature cell (spans 2 cols, 2 rows)
feature=I(bento, {
  type: "frame",
  width: 584,
  height: 400,
  cornerRadius: [16, 16, 16, 16],
  fill: "#1A1A1A"
})

// Small cells
cell1=I(bento, {
  type: "frame",
  width: 284,
  height: 192,
  cornerRadius: [12, 12, 12, 12],
  fill: "#F5F5F5"
})
cell2=I(bento, {
  type: "frame",
  width: 284,
  height: 192,
  cornerRadius: [12, 12, 12, 12],
  fill: "#E8E8E8"
})
```

## Using Design System Components

When design system components are available:

```javascript
// First, search for available components
// mcp__pencil__batch_get({ patterns: [{ reusable: true }] })

// Insert component instance
card=I(parent, {type: "ref", ref: "CardComponent"})

// Customize instance properties
U(card+"/title", {content: "Custom Title"})
U(card+"/description", {content: "Custom description"})
U(card, {width: 400})

// Replace slot content
newContent=R(card+"/imageSlot", {
  type: "frame",
  fill: {type: "linear", stops: [...]}
})
```

## Error Prevention

1. **Always create bindings** for Insert/Copy/Replace
2. **Never reference copied descendant IDs** - use `descendants` in Copy instead
3. **Keep batches under 25 operations**
4. **Validate parent exists** before inserting
5. **Use document for top-level frames**, frame IDs for nested content


path: references/dna-to-pencil.md
content: # DNA to Pencil Property Mapping

Complete reference for translating aesthetic DNA codes to Pencil .pen file properties.

## Layout Axis

### centered
```javascript
{
  layout: "vertical",
  alignItems: "center",
  padding: [64, 48, 64, 48],  // symmetric
  gap: 24
}
```

### asymmetric
```javascript
{
  layout: "horizontal",
  alignItems: "flex-start",
  padding: [80, 120, 64, 48],  // varied
  gap: 32
}
// Child positioning: use x/y offsets, varied widths
```

### grid-breaking
```javascript
{
  layout: "none",  // absolute positioning
}
// Children use explicit x, y coordinates
// Overlapping elements, diagonal compositions
```

### full-bleed
```javascript
{
  width: "fill_container",
  height: "fill_container",
  padding: [0, 0, 0, 0]
}
```

### bento
```javascript
{
  layout: "grid",
  columns: 4,
  rows: 3,
  gap: 16
}
// Children use colspan, rowspan for varied cell sizes
```

### editorial
```javascript
{
  layout: "vertical",
  alignItems: "flex-start",
  padding: [96, 64, 96, 200],  // generous margins
  maxWidth: 720
}
```

## Color Axis

### dark
```javascript
// Frame fills
fill: "#0A0A0A"  // near-black
// or use variables
fill: "var(--color-background-dark)"

// Text colors
textColor: "#FAFAFA"  // near-white
```

### light
```javascript
fill: "#FEFEFE"  // near-white
textColor: "#1A1A1A"  // near-black
```

### monochrome
```javascript
// Limited palette: black, white, 2-3 grays
fill: "#FFFFFF"
// Accent via stroke weight, not color
stroke: "#000000"
strokeThickness: 2
```

### gradient
```javascript
fill: {
  type: "linear",
  angle: 135,
  stops: [
    { position: 0, color: "#1E3A5F" },
    { position: 1, color: "#0A1628" }
  ]
}
```

### high-contrast
```javascript
fill: "#000000"
textColor: "#FFFFFF"
// No mid-tones, stark opposites
```

### brand-tinted
```javascript
// Neutrals tinted with brand hue
fill: "oklch(0.98 0.01 250)"  // very light, slight blue
textColor: "oklch(0.15 0.02 250)"  // near-black, slight blue
```

## Typography Axis

### display-heavy
```javascript
// Headlines dominate
{
  type: "text",
  content: "Hero Title",
  fontSize: 72,
  fontWeight: "900",
  fontFamily: "Playfair Display"
}
// Body much smaller
{
  fontSize: 16,
  fontWeight: "400"
}
```

### text-forward
```javascript
// Body text prominence
{
  fontSize: 20,
  lineHeight: 1.7,
  fontFamily: "Georgia"
}
// Headlines restrained
{
  fontSize: 32,
  fontWeight: "600"
}
```

### minimal
```javascript
// Single family, tight scale
fontFamily: "Helvetica Neue"
// All weights from same family
// Scale: 14, 16, 20, 24, 32 (1.25 ratio)
```

### expressive
```javascript
// Mixed families, decorative
// Headline: Display serif
// Body: Geometric sans
// Accents: Handwritten or display
{
  fontFamily: "Fraunces",
  fontWeight: "400",
  fontStyle: "italic"
}
```

### editorial
```javascript
// Magazine-style hierarchy
// Large drop caps, pull quotes
// Serif headings, sans body or vice versa
{
  firstLetter: {
    fontSize: 96,
    float: "left"
  }
}
```

## Motion Axis

**Note:** Pencil is static. Motion DNA informs placeholder/documentation.

| Motion Value | Design Implication |
|-------------|-------------------|
| orchestrated | Placeholder for animation sequence notes |
| subtle | Minimal transition indicators |
| aggressive | Bold state changes (show both states) |
| none | No animation planned |
| scroll-triggered | Section reveals on scroll (show waypoints) |

## Density Axis

### spacious
```javascript
{
  gap: 48,
  padding: [80, 64, 80, 64]
}
// Generous whitespace
// Few elements per section
```

### compact
```javascript
{
  gap: 12,
  padding: [24, 20, 24, 20]
}
// Tight layout
// Information-dense
```

### mixed
```javascript
// Vary by section
// Hero: spacious
// Data: compact
// CTA: spacious
```

### full-bleed (density)
```javascript
{
  padding: [0, 0, 0, 0],
  gap: 0
}
// Edge-to-edge content
```

## Background Axis

### solid
```javascript
fill: "#FFFFFF"
// Single color, no complexity
```

### gradient
```javascript
fill: {
  type: "linear",
  angle: 180,
  stops: [
    { position: 0, color: "#F8FAFC" },
    { position: 1, color: "#E2E8F0" }
  ]
}
```

### textured
```javascript
// Use G() operation to apply texture
G("frameId", "stock", "subtle noise texture background")
// or
G("frameId", "ai", "subtle paper texture warm beige")
```

### patterned
```javascript
// Use G() for pattern
G("frameId", "ai", "geometric pattern minimal black white")
// or SVG pattern fill
```

### layered
```javascript
// Multiple overlapping frames
// Background layer + decorative shapes + content
bg=I(parent, {type: "frame", fill: "#0A0A0A"})
accent=I(parent, {type: "ellipse", fill: "gradient", opacity: 0.5})
content=I(parent, {type: "frame", fill: "transparent"})
```

## Complete DNA → Properties Example

DNA: `[asymmetric, dark, display-heavy, orchestrated, spacious, gradient]`

```javascript
// Container
container=I(document, {
  type: "frame",
  name: "Hero-Asymmetric-Dark",
  width: 1440,
  height: 900,
  layout: "horizontal",
  alignItems: "flex-start",
  padding: [120, 80, 80, 160],
  gap: 64,
  fill: {
    type: "linear",
    angle: 135,
    stops: [
      { position: 0, color: "#1E293B" },
      { position: 1, color: "#0F172A" }
    ]
  }
})

// Headline (display-heavy)
heading=I(container, {
  type: "text",
  content: "Headline Text",
  fontSize: 80,
  fontWeight: "900",
  fontFamily: "Playfair Display",
  textColor: "#F8FAFC",
  width: 600
})

// Body text (subordinate)
body=I(container, {
  type: "text",
  content: "Supporting copy...",
  fontSize: 18,
  fontWeight: "400",
  fontFamily: "Inter",
  textColor: "#94A3B8",
  width: 400,
  lineHeight: 1.6
})
```

## Style Guide Tag Mapping

Map DNA to Pencil style guide tags for `get_style_guide`:

| DNA | Tags |
|-----|------|
| dark | dark-mode, moody, dramatic |
| light | light, clean, airy |
| gradient | vibrant, colorful |
| spacious | whitespace, minimal, airy |
| compact | dense, data-heavy |
| display-heavy | bold, impactful |
| editorial | magazine, publication |
| brutalist | raw, industrial |


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: clean (LOW)