skills/canvas-sync/SKILL.md
Scan codebase and sync design tokens, components, and conventions to Pencil canvas — or start from scratch using Pencil style guides when no codebase exists. Use when user says "sync canvas", "configure pencil", "setup pencil", "canvas sync", "sync tokens to pencil", "design from scratch", "pick a style guide", or wants Pencil to generate project-aware code. Also triggers on "build component library", "pencil components", "start designing", or when mockup quality from Pencil is poor.
npx skillsauth add ash4180/vorbit canvas-syncInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scan a codebase to detect its stack, extract design tokens, build a component inventory, create reusable Pencil components on canvas, and write project rules so AI-generated mockups and code match project conventions.
.claude/rules/ and Pencil canvas only.Read and follow _shared/mcp-tool-routing.md (glob for **/skills/_shared/mcp-tool-routing.md).
ToolSearch for "pencil" to check if Pencil MCP is available/mcp to connect, then retry." → STOPmcp__pencil__get_editor_state to verify connection/mcp to reconnect, then retry." → STOP.pen file is currently open (needed for Phases 4-5)Goal: Detect framework, styling, component library, icon library, and target platform with screen sizes.
Read the detection tables at skills/canvas-sync/references/detection.md.
Actions:
Create todo list with all phases (0-6)
Check flags:
--refresh: Read existing .claude/rules/canvas-sync.md, skip to Phase 2 using cached stack config. No confirmations until final sync preview.--components-only: Skip token extraction (Phase 2), go straight to Phase 3.Read package.json (if it exists) to detect dependencies
Apply detection rules for: Framework, Styling, Component library, Icon library [Skill ref: detection.md]
Detect platform and screen sizes — two paths:
Path A — Project detected (package.json or framework signals found):
skills/canvas-sync/references/platforms/{detected}.md [Skill ref]
platforms/mobile.mdplatforms/web.mdplatforms/desktop.mdplatforms/custom.mdPath B — No project or ambiguous platform (no package.json, plain HTML, or framework like React that could be web or mobile):
Step 1 — Ask platform [User]:
What platform are you designing for?
1. Web (Desktop + Tablet + Mobile)
2. iOS (iPhone + iPad)
3. Android
4. iOS + Android (cross-platform mobile)
5. Desktop App (Electron, Tauri, native)
6. Marketing / Social Media (banners, posts, stories)
7. Custom size (specify exact dimensions)
8. Multiple / All platforms
Any specific devices or sizes? (e.g., iPhone 16 Pro, 1200×630 OG image, 728×90 banner)
platforms/{chosen}.md)detection.md. No safe areas, no bone needed (single canvas).Step 2 — Select design system from Pencil [Pencil]:
Since there's no codebase to extract tokens from, use Pencil's built-in style guides as the token source:
mcp__pencil__get_style_guide_tags to get available style tags[User] — present the tags and ask:
What visual style do you want? Pick a few keywords or describe:
e.g., minimal + dark-mode, brutalist + bold, calm + soft, elegant + modern...
Available directions: [show relevant subset of tags from get_style_guide_tags]
Or say "show me options" and I'll preview a few.
mcp__pencil__get_style_guide(tags) with the user's chosen tags + platform tag (e.g., mobile, webapp)
[User] — show the style guide preview:
Style: [name] — [description summary]
Colors: [count] ([list key names])
Typography: [families] at [count] sizes
Spacing: [scale summary]
Icons: [library]
Use this design system? Or want to see another? (say different tags to try again)
mcp__pencil__get_style_guide(name="[name]")Present findings and use AskUserQuestion to confirm:
Path A (codebase detected) — show full stack:
Stack detected:
Framework: React Native
Styling: RN StyleSheet + Theme Object
Components: Custom (14 found)
Icons: Lucide
Platform: iOS + Android
Screens: iPhone (390×844), Android (412×915), Android compact (360×780)
Page layout: Pattern A — Header + Scroll + Bottom Nav
Is this correct? Adjust anything?
Path B (no codebase) — show platform + selected design system:
Platform: iOS + Android
Screens: iPhone 16 Pro (393×852), Pixel 8 (412×924)
Page layout: Pattern A — Header + Scroll + Bottom Nav
Design system: [style guide name] ([key aesthetic summary])
This will:
1. Sync design system tokens as Pencil variables
2. Create Screen Shells for both devices
3. Build bone demo with the layout above
Is this correct? Adjust screens, layout, or style?
Path B omits Framework, Styling, Components, Icons — those only matter when a codebase exists. The design system comes from Pencil's style guide, not code.
This is Confirmation 1 of 2. The user can adjust screens, page layout pattern, etc.
Output: Confirmed stack configuration
Goal: Extract design tokens from project theme/config files.
Path B (no codebase): Skip this phase entirely. Tokens come from the Pencil style guide selected in Phase 1 Step 2.
Actions [Codebase]:
[Skill ref] to find token files@theme block in main CSStailwind.config.* theme object:root custom propertiessrc/theme/index.ts first, then each sub-module (colors, spacing, typography, borderRadius, shadows). Extract exported objects — these ARE the tokens.Do NOT ask for confirmation here. Tokens are shown in the combined preview (Phase 3).
Output: Categorized token list with usage context
Goal: Extract component information so Pencil builds with existing components.
Path B (no codebase): Skip this phase entirely. No components to inventory.
CRITICAL: Read actual source files, not just filenames.
Follow the "Component Inventory Scan" section in detection.md [Skill ref]:
tsconfig.json / jsconfig.json / components.json_-prefixed)ui/ and common/.Combined sync preview — Confirmation 2 of 2:
Path A (codebase detected):
Ready to sync to Pencil canvas:
Platform: iOS + Android
Screens: iPhone 16 Pro (393×852), iPhone SE (375×667), Pixel 8 (412×924)
Tokens (23):
Colors: 12 (primary, secondary, background, foreground, ...)
Spacing: 8 (0, 1, 2, 3, 4, 6, 8, 12)
Typography: 3 families, 7 sizes
Borders: 4 radii
Shadows: 3 levels
Components (14):
Button 6 variants, 4 sizes
Card 5 sub-components
Dialog compound (7 parts)
Input 3 required props
...
This will:
1. Sync tokens as Pencil variables
2. Build reusable components on canvas (top 10)
3. Write project rules with screen presets to .claude/rules/canvas-sync.md
Proceed?
Path B (no codebase) — skip Confirmation 2 entirely. There are no tokens to extract from code and no components to inventory. The style guide tokens (selected in Phase 1 Step 2) go straight to Phase 4. The user already approved screens, layout, and design system in Confirmation 1.
Use AskUserQuestion with this preview (Path A only). User can adjust what gets synced.
Output: Confirmed extraction ready for sync
Goal: Push tokens into the active .pen file as Pencil variables.
Token source depends on the path:
[Pencil]Actions:
.pen file is open (from Phase 0):
mcp__pencil__get_editor_state again [Pencil]mcp__pencil__get_variables [Pencil] to read existing canvas variablesmcp__pencil__set_variables [Pencil] with the token payloadToken format for set_variables:
Output: Tokens synced to canvas
Goal: Create reusable Pencil components that future mockups can copy/instance instead of rebuilding from scratch.
This is the key performance improvement. Without this phase, every mockup builds every Button, Card, Input from raw primitives. With reusable components, mockups use I(parent, {type: "ref", ref: "btnId"}) — one operation instead of 8-12.
First, read skills/canvas-sync/references/layout-model.md [Skill ref] — the CSS→Pencil mental model. This teaches how block vs inline elements work, when to wrap text in frames, how flex layout maps to Pencil, and platform safe area rules. Understanding this prevents layout bugs at the source.
Then load the appropriate Pencil guidelines based on the path and detected platform:
Path A (codebase — building reusable components):
mcp__pencil__get_guidelines("design-system") [Pencil] — how to structure reusable components (naming, slots, nesting)mcp__pencil__get_guidelines("mobile-app") [Pencil]mcp__pencil__get_guidelines("web-app") [Pencil]Path B (no codebase — building Screen Shells + bone demo only):
get_guidelines("design-system") — no reusable components to build yetmcp__pencil__get_guidelines("mobile-app") [Pencil]mcp__pencil__get_guidelines("web-app") [Pencil]Call mcp__pencil__batch_get with patterns: [{ reusable: true }] and searchDepth: 2 to find any existing reusable components on canvas.
Call mcp__pencil__find_empty_space_on_canvas to position the component library frame away from existing content.
Build a "Component Library" container frame on canvas, then populate it with reusable components.
Priority order:
Always build first — Screen Shells (one per detected screen size):
Screen Shells are the foundation every mockup starts from. For device screens (mobile, tablet), they include safe area padding. For non-device targets (banners, social media, custom sizes, desktop apps), they are simple frames with no safe areas — just the correct dimensions. See "Screen Shell Recipe" below.
Then build the PROJECT's actual components — not a generic UI kit.
Do NOT use a fixed list of generic components (Button, Input, Card, etc.). Instead:
import.*from across component files. The most-imported components are the ones mockups will need most.@react-navigation/bottom-tabs) or a top navigation bar, build those as reusable components too. Every screen needs navigation.Cap at 15 reusable components total (including Screen Shells) to keep batch_design calls manageable.
For each component — read the source, don't guess:
The component's StyleSheet.create() block (or equivalent styles) IS the design spec. Read it and map every property to Pencil equivalents using the "StyleSheet → Pencil Mapping" table in the detection/platform reference.
StyleSheet.create() for exact padding, gap, borderRadius, colors, typographyreusable: true frame matching the codebase component name$--color-primary) for colors that come from theme hooks — use the token name that matches the theme key (e.g., colors.card → $--color-card)Showcase layout — organize the component library vertically with labeled sections:
Component Library (vertical layout, gap: 60)
├── "Screen Shells" label
│ └── shells row (horizontal, gap: 40)
├── "Buttons" label
│ └── variants row (horizontal, gap: 16)
├── "Badges" label
│ └── variants row (horizontal, gap: 16)
├── "Cards" label
│ └── card variants (horizontal, gap: 24)
└── ... more sections
A Screen Shell is a reusable frame representing a real device screen with safe area regions built in. Every mockup starts by instancing a Screen Shell, then inserting content into the content slot.
Structure:
Screen Shell [device-name] (reusable, vertical, clip: true)
├── status-bar (frame, fixed height = top safe area)
├── content (frame, vertical, fill_container height, placeholder: true)
└── home-indicator (frame, fixed height = bottom safe area)
Safe area values (from detection/platform reference "Safe Areas" section):
Build example (iOS, iPhone standard 390×844, Dynamic Island):
I(parent, {type: "frame", reusable: true, name: "Screen Shell - iPhone", width: 390, height: 844, layout: "vertical", fill: "$background", clip: true})
I(shell, {type: "frame", name: "status-bar", width: "fill_container", height: 59, fill: "$background"})
I(shell, {type: "frame", name: "content", width: "fill_container", height: "fill_container", layout: "vertical", placeholder: true})
I(shell, {type: "frame", name: "home-indicator", width: "fill_container", height: 34, fill: "$background"})
Build example (Android, flagship 412×915, gesture nav):
I(parent, {type: "frame", reusable: true, name: "Screen Shell - Android", width: 412, height: 915, layout: "vertical", fill: "$background", clip: true})
I(shell, {type: "frame", name: "status-bar", width: "fill_container", height: 24, fill: "$background"})
I(shell, {type: "frame", name: "content", width: "fill_container", height: "fill_container", layout: "vertical", placeholder: true})
I(shell, {type: "frame", name: "nav-bar", width: "fill_container", height: 16, fill: "$background"})
Build example (Web, desktop 1440×900):
I(parent, {type: "frame", reusable: true, name: "Screen Shell - Desktop", width: 1440, height: 900, layout: "vertical", fill: "$background", clip: true})
I(shell, {type: "frame", name: "content", width: "fill_container", height: "fill_container", layout: "vertical", placeholder: true})
Usage in mockups — always build the Screen Bone first:
Instance the shell, then build a "bone" skeleton inside the content slot BEFORE placing any components. The bone defines the page's structural zones (header, scroll area). Floating elements (tab bars, FABs) go in a SEPARATE nav-overlay that stacks on top of the bone. See "Screen Bone Pattern" in the detection/platform reference for the full recipe and patterns.
For screens with floating nav (Pattern A — most common):
screen=I(parent, {type: "ref", ref: "[shell-id]"})
U(screen+"/content", {layout: "none"})
bone=I(screen+"/content", {type: "frame", name: "bone", layout: "vertical", width: 393, height: 759, x: 0, y: 0})
header=I(bone, {type: "frame", name: "header", height: "fit_content", ...})
scroll=I(bone, {type: "frame", name: "scroll-area", height: "fill_container", scroll: true, ...})
nav=I(screen+"/content", {type: "frame", name: "nav-overlay", layout: "none", width: 393, height: 759, x: 0, y: 0})
Calculate dimensions: width = shell_width, height = shell_height - status_bar - home_indicator. fill_container does NOT work inside layout: "none" — always use explicit pixels for bone and nav-overlay.
Then fill each zone with components:
I(header, {type: "text", content: "Hey, Ash", ...})
I(scroll, {type: "ref", ref: "[card-id]"})
I(nav, {type: "ref", ref: "[tab-bar-id]", x: 75, y: 653})
Key: the content slot uses layout: "none" (stacking mode) so bone and nav-overlay both fill the same space. The bone handles vertical flow. The nav-overlay floats on top with absolute positioning. Never put the nav-overlay INSIDE the bone — it will compete for space with fill_container siblings.
For screens without floating elements (Pattern B-simple, C, D):
No stacking needed — leave the content slot as layout: "vertical" (or "horizontal" for sidebar layouts) and build the bone directly.
CRITICAL — Review layout model and syntax before building:
Before writing ANY batch_design call, review the checklist at the bottom of references/layout-model.md. Key rules:
width/height — wrap in a frame when you need sizing control (see "When to Wrap")fill_container only works inside flex parents — not inside layout: "none" (see "Flexbox" section)padding: [t,r,b,l] — NOT paddingTop/paddingLeft (silently dropped)stroke: {align: "inside", fill: "$--border", thickness: 1} — NOT stroke: "$--border", strokeWidth: 1type: "icon_font" with iconFontFamily: "lucide", iconFontName: "home" — NOT type: "icon"get_guidelines("design-system") first — it has the authoritative Pencil schema with examples.Batch strategy — keep each batch_design call under 25 operations:
After all batches: Take a screenshot of the full component library frame to verify it looks correct. Each section should be visually labeled and organized.
After building, call mcp__pencil__batch_get on the library frame to get the real node IDs of each reusable component. These IDs are needed for the rules file so future mockup instructions can reference them.
Save a mapping like:
Button: "abc123"
Card: "def456"
Input: "ghi789"
Output: Reusable component library on canvas with known IDs
Goal: Write .claude/rules/canvas-sync.md with both code generation AND Pencil mockup rules.
Actions:
# Canvas Sync: Project Conventions
Generated by canvas-sync v2. Re-run with --refresh to update.
## Stack
- Framework: [detected]
- Styling: [detected]
- Component Library: [detected]
- Icon Library: [detected]
- Platform: [detected — e.g., iOS + Android, Web, etc.]
## Screen Presets
When creating mockups in .pen files, **always start by instancing a Screen Shell** — never create bare frames at arbitrary sizes.
### Available Screen Shells
| Shell | ID | Width × Height | Safe Areas (top / bottom) | Use For |
|-------|-----|---------------|--------------------------|---------|
| [primary device name] | `[pencil-node-id]` | [w] × [h] | [top]pt / [bottom]pt | Primary mockups |
| [secondary device name] | `[pencil-node-id]` | [w] × [h] | [top]pt / [bottom]pt | [purpose] |
| [compact device name] | `[pencil-node-id]` | [w] × [h] | [top]pt / [bottom]pt | Compact layout testing |
### How to use
screen=I(parent, {type: "ref", ref: "[shell-id]"}) I(screen+"/content", { ... your page content ... })
The `content` slot has `placeholder: true` — insert all page content there. The status bar and home indicator regions are already sized correctly. Never place content outside the `content` slot.
## Code Generation Rules
- Import components from [detected paths]
- Use [detected styling approach] for all styling
- Use [detected icon library] for icons
- Follow [framework] conventions for file structure
## Pencil Mockup Rules
When building mockups in .pen files, use these reusable components instead of raw frames.
### Component Library Reference
[For each component built in Phase 5:]
**[ComponentName]** — ID: `[pencil-node-id]`
- Insert: `I(parent, {type: "ref", ref: "[pencil-node-id]"})`
- Override label: `U(instance+"/label", {content: "New Text"})`
- Variants: [list variant names if built as separate reusable components]
### Token Usage Map
Map project tokens to visual properties:
- **Button background:** $--color-primary
- **Button text:** $--color-primary-foreground
- **Card background:** $--color-card
- **Card border:** $--color-border, radius: $--radius-lg
- **Body text:** $--font-sans, $--text-base
- **Heading text:** $--font-sans, $--text-xl, weight: 600
- **Standard padding:** $--spacing-4 (16px)
- **Standard gap:** $--spacing-3 (12px)
[... derived from Phase 2 token-to-usage mapping]
### Layout Patterns — Screen Bone (REQUIRED)
Every mockup screen MUST use the Screen Bone pattern. Build the structural skeleton first, then fill zones with components.
**Bone structure** (insert inside Screen Shell's `content` slot):
U(screen+"/content", {layout: "none"}) ← stacking mode for floating elements bone=I(screen+"/content", {..., layout: "vertical", width: [W], height: [H], x:0, y:0}) ← explicit size (fill_container won't work in layout:none) header=I(bone, {..., height: "fit_content"}) ← fixed header zone scroll=I(bone, {..., height: "fill_container", scroll: true}) ← scrollable content (fill_container OK — bone IS flex parent) nav=I(screen+"/content", {..., layout: "none", width: [W], height: [H], x:0, y:0}) ← floating overlay (SIBLING of bone, not child)
Where `W = shell_width`, `H = shell_height - status_bar - home_indicator`.
Read the target screen file to determine which zones exist. See detection/platform reference "Screen Bone Pattern" for full patterns (A/B/C).
Common content layouts within zones:
- **Card stack:** vertical layout, gap: [detected]
- **Card grid:** horizontal wrap, gap: [detected], min-width per card: [detected]
- **Form stack:** vertical layout, gap: [detected]
## Design Tokens
Synced to Pencil canvas as variables:
- Colors: [list token names]
- Spacing: [list token names]
- Typography: [list token names]
## Component Inventory
[For each component — same format as v1:]
### [ComponentName]
- **Import:** `import { [Name] } from "[path]"`
- **Variants:** [prop] ([values]), ...
- **Defaults:** [prop]="[default]", ...
- **Required props:** [prop]: [type], ...
- **Optional props:** [prop]?: [type], ...
- **Sub-components:** [if compound]
- **Pattern:** [Shadcn/cva | Radix compound | MUI wrapper | Custom]
- **Pencil ref:** `[pencil-node-id]` (use for mockups)
.claude/rules/canvas-sync.mdCanvas sync complete:
Stack: Next.js + Tailwind v4 + Shadcn + Lucide
Platform: Web (Desktop 1440×900, Tablet 768×1024, Mobile 390×844)
Tokens: 23 synced to Pencil canvas
Components: 14 inventoried, 10 built as reusable Pencil components
Rules: .claude/rules/canvas-sync.md written
Pencil mockups will now use existing components via copy/instance.
Screen presets ensure mockups match real device dimensions.
Run --refresh after changing tokens or adding components.
--refresh).claude/rules/canvas-sync.md for cached stack config--components-only).claude/rules/canvas-sync.md for cached stack + tokensget_guidelines("design-system") firstpaddingTop/paddingLeft/strokeWidth in batch_design — these are silently dropped. Use padding: [v, h] and stroke: {align, fill, thickness} instead. Check the detection/platform reference "silently drop" table.U() on descendants of C() nodes (IDs change on copy)HomeScreen.tsx) to understand the exact layout hierarchy. Don't add header icons, titles, or sections that aren't in the source. The screen file defines what components appear and in what order — follow it, don't embellish.development
Sync design tokens and components from a codebase to a Pencil canvas (`.pen` files), or set up a Pencil canvas from a style guide when no codebase exists. Use when the user says "sync pencil", "setup pencil", "configure pencil", "pencil sync", "sync tokens to pencil", "build pencil component library", or names Pencil/`.pen` files explicitly. Also triggers when mockups generated by Pencil don't match project conventions.
development
--- name: figma version: 1.6.0 description: Use when user says "figma", "figma it", "sync figma", "figma mockup", "create figma file", "design to figma", "figma from PRD", "figma from journey", "build in figma", or "figma design system" — anything that creates, syncs, or updates Figma design systems, components, variables, mockups, or front-end-ready screens. Always enumerates the linked Figma library FIRST (library-driven discovery, not per-need search), produces a block→DS mapping table for us
development
Use when the user wants to build Webflow pages, templates, or components, with or without Figma designs as reference.
testing
Use when the user wants to verify an implementation, validate acceptance criteria, or run a Vorbit-style post-change check using shared project rules.