name: penpot-workflow description: > Manage design work in Penpot — create projects, organize pages, build components, export assets, review designs visually, and handle design-to-code handoff. Use when asked to create designs, manage Penpot projects, build components, export tokens, set up a new design project, review a design, take screenshots, or show what something looks like.
Penpot Workflow
Step-by-step workflows for managing design work in Penpot via the pi-penpot extension.
Project Setup
When starting a new design project:
- Create the Penpot project with a clear, matching name (same as the code repo)
- Set up standard pages:
Design System— shared components, tokens, and stylesWireframes— low-fidelity layouts and flows- One page per major screen or user flow (e.g.,
Dashboard,Settings,Onboarding)
- Define foundations first:
- Color palette (primary, secondary, neutral, semantic: success/warning/error/info)
- Typography scale (headings h1-h6, body, caption, label, mono)
- Spacing scale (aligned with Tailwind: 4px base unit)
- Border radius tokens
- Shadow/elevation tokens
Shape Creation
Creating shapes with full styling
Use penpot_page tool actions. All shapes support fills, opacity, and rotation on creation.
Rectangles and frames also support r1-r4 for border radius on creation:
penpot_page add-rectangle fileId=X pageId=Y x=0 y=0 width=200 height=100
fills=[{fillColor: "#1E293B", fillOpacity: 1}]
r1=12 r2=12 r3=12 r4=12
Text supports fontSize, fontWeight, fontFamily, fontColor on creation:
penpot_page add-text fileId=X pageId=Y x=0 y=0 width=200 height=40
text="Hello World"
fontSize="24" fontWeight="700" fontFamily="sourcesanspro" fontColor="#FFFFFF"
Available font families
sourcesanspro— Source Sans Pro (default, always available)- Custom fonts can be uploaded via
create-font-variant
Font size and weight are STRINGS
Always pass fontSize and fontWeight as strings: "24" not 24, "700" not 700.
Shape Styling (modify-shape)
After creation, use modify-shape to add advanced styling. First-class params:
Border Radius
penpot_page modify-shape fileId=X pageId=Y shapeId=Z r1=16 r2=16 r3=16 r4=16
r1= top-left,r2= top-right,r3= bottom-right,r4= bottom-left
Shadows
penpot_page modify-shape fileId=X pageId=Y shapeId=Z
shadow=[{
style: "drop-shadow",
color: {color: "#000000", opacity: 0.3},
offsetX: 0, offsetY: 4, blur: 12, spread: 0
}]
- Styles:
drop-shadow,inner-shadow - UUID is auto-generated if not provided
- Multiple shadows supported (array)
Blur
penpot_page modify-shape fileId=X pageId=Y shapeId=Z
blur={type: "layer-blur", value: 4}
- Types:
layer-blur,background-blur - UUID is auto-generated
Strokes
penpot_page modify-shape fileId=X pageId=Y shapeId=Z
strokes=[{strokeColor: "#7C3AED", strokeOpacity: 0.5, strokeWidth: 2, strokeAlignment: "inner"}]
- Alignment:
inner,center,outer - Style (via attrs):
solid,dotted,dashed,mixed,none
Text Content (re-style existing text)
penpot_page modify-shape fileId=X pageId=Y shapeId=Z
textContent={
type: "root",
children: [{type: "paragraph-set", children: [{type: "paragraph", children: [{
text: "Updated text",
fontFamily: "sourcesanspro",
fontSize: "32",
fontWeight: "700",
fontStyle: "normal",
fillColor: "#FFFFFF",
fillOpacity: 1
}]}]}]
}
Generic attrs (any shape attribute)
Use attrs as a catch-all for any shape property not covered above:
penpot_page modify-shape fileId=X pageId=Y shapeId=Z
attrs={hidden: true, blocked: false}
Design Workflow — Best Practices
Step-by-step for polished designs
- Create shapes with basic geometry + fills + border radius
- Style shapes with modify-shape: shadows, strokes, blur
- Create text with proper fontSize/fontWeight/fontColor from the start
- Re-style text with textContent on modify-shape if needed later
- Batch work — create all shapes first, then style them (reduces API calls)
Performance tips
- Create shapes in rapid succession (each is one API call)
- Use modify-shape to batch multiple styling changes on one shape (fills + strokes + shadow in one call)
- Always get file features from
penpot get-filebefore manual API calls
What works with the extension tools
| Feature | Create | Modify | Notes |
|---|---|---|---|
| Position & size | ✅ | ✅ | x, y, width, height |
| Fills | ✅ | ✅ | Array of fill objects |
| Border radius | ✅ | ✅ | r1, r2, r3, r4 params |
| Shadows | ❌ | ✅ | shadow param, UUID auto-generated |
| Blur | ❌ | ✅ | blur param, UUID auto-generated |
| Strokes | ✅ | ✅ | strokes param |
| Text styling | ✅ | ✅ | fontSize/fontWeight/fontColor on create; textContent on modify |
| Opacity | ✅ | ✅ | 0-1 number |
| Rotation | ✅ | ✅ | degrees |
Visual Review
When you need to review your own work or the user asks you to review a design, capture actual rendered screenshots from Penpot's viewer.
When to do this
- After creating or modifying designs — always screenshot to verify the result
- When the user says "review", "show me", "how does it look", "screenshot"
- Before handoff — capture final state for documentation
Workflow
1. Get the file and page IDs
penpot get-file fileId=<fileId>
2. Create a share link
penpot create-share-link fileId=<fileId> pages=[<pageId1>, <pageId2>] whoInspect="all"
Share links are required — Penpot's viewer won't render without either authentication or a share token.
3. Screenshot each page with Playwright
The Penpot viewer URL format is:
https://penpot.e9n.dev/#/view?file-id=<fileId>&page-id=<pageId>§ion=interactions&index=0&share-id=<shareId>
Use Playwright to screenshot each page URL. Penpot is a complex ClojureScript SPA — wait 6+ seconds after navigation for it to render.
Cleanup: Always close the Playwright browser after capture to prevent leaking Chromium processes:
await browser.close();
Save screenshots to /tmp/penpot-<page-name>.png, then read them to view inline. After viewing, delete the temporary file to avoid accumulating disk usage:
rm /tmp/penpot-<page-name>.png
4. Review what you see
After viewing the screenshot, evaluate:
- Layout — is there wasted space? Are elements aligned to a grid?
- Visual hierarchy — can you immediately tell what's important?
- Consistency — do similar elements (cards, tags, buttons) share the same styling?
- Completeness — are there missing states, empty areas, placeholder content?
- Accessibility — text contrast, touch target sizes, focus indicators
- Polish — border radius consistency, shadow consistency, proper spacing
Component Creation
Follow atomic design methodology:
Atoms (smallest building blocks)
- Buttons (primary, secondary, ghost, destructive — each with default/hover/active/disabled/focus)
- Inputs (text, textarea, select, checkbox, radio, toggle)
- Labels, badges, tags
- Icons (use Lucide as base set)
- Avatar, tooltip, separator
Molecules (composed atoms)
- Form fields (label + input + helper text + error message)
- Search bar (input + icon + button)
- Card (container + content slots)
- Menu item (icon + label + shortcut badge)
Organisms (composed molecules)
- Navigation (sidebar, topbar, breadcrumbs)
- Data tables (headers + rows + pagination + filters)
- Modals/dialogs (overlay + card + action buttons)
- Forms (multiple form fields + submit action)
Naming Convention
Use slash-separated categories: category/component-name/variant
buttons/primary/defaultbuttons/primary/hoverforms/text-input/fillednavigation/sidebar/collapsed
States & Variants
Every interactive component needs these states:
- Default — resting state
- Hover — mouse over (desktop)
- Active/Pressed — being clicked/tapped
- Focus — keyboard focus (visible focus ring, WCAG required)
- Disabled — non-interactive
- Loading — async operation in progress (skeleton or spinner)
- Error — validation failure
Responsive Design
Design at these breakpoints (aligned with Tailwind):
- Mobile: 375px (iPhone SE baseline)
- Tablet: 768px
- Desktop: 1280px
- Wide: 1536px
Use Penpot's grid and layout features:
- Auto-layout for flex-like behavior
- Grid components for dashboard layouts
- Constraints for responsive positioning
Design-to-Code Handoff
When preparing designs for developer handoff:
Export design tokens as JSON:
{ "colors": { "primary-500": "#7c6ff0", ... }, "spacing": { "1": "4px", "2": "8px", ... }, "typography": { "heading-1": { "size": "36px", "weight": 700, "lineHeight": 1.2 } }, "radii": { "sm": "4px", "md": "8px", "lg": "12px" }, "shadows": { "sm": "0 1px 2px rgba(0,0,0,0.05)", ... } }Map components to shadcn-svelte — document which Penpot component maps to which shadcn component and what customization is needed
Annotate specs — add notes on each page for:
- Spacing between elements
- Interaction behavior (transitions, animations)
- Edge cases (empty states, error states, loading states)
- Accessibility notes (ARIA labels, focus order)
Export assets — SVGs for icons and illustrations, optimized images
Penpot API Patterns
Use the pi-penpot extension's three tools:
penpot— org-level: projects, files, teams, libraries, webhooks, snapshots, share linkspenpot_page— design: pages, shapes (create/modify/delete/move), componentspenpot_comment— collaboration: comment threads and replies
Critical API knowledge
- Always get the file first —
penpot get-filereturns pageIds and file features - File features must match — features from
get-filemust be passed back inupdate-file - Transit+JSON encoding — the extension handles this automatically for all write operations
- UUIDs auto-generated — shadow and blur IDs are auto-generated when not provided
- camelCase in params, kebab-case internally — the extension converts automatically
Checklist Before Handoff
- All components use the design system (no one-off styles)
- States documented (default, hover, active, focus, disabled, error, loading)
- Responsive layouts at all breakpoints
- Color contrast passes WCAG AA (4.5:1 text, 3:1 large text/UI)
- Focus indicators visible on all interactive elements
- Empty states, error states, and loading states designed
- Design tokens exported as JSON
- Component-to-shadcn mapping documented
- Spacing and typography use the defined scale (no magic numbers)