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 styles
Wireframes — 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-file before 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/default
buttons/primary/hover
forms/text-input/filled
navigation/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 links
penpot_page — design: pages, shapes (create/modify/delete/move), components
penpot_comment — collaboration: comment threads and replies
Critical API knowledge
- Always get the file first —
penpot get-file returns pageIds and file features
- File features must match — features from
get-file must be passed back in update-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
1---2name: penpot-workflow3description: 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.4---56# Penpot Workflow78Step-by-step workflows for managing design work in Penpot via the `pi-penpot` extension.910## Project Setup1112When starting a new design project:13141. **Create the Penpot project** with a clear, matching name (same as the code repo)152. **Set up standard pages:**16 - `Design System` — shared components, tokens, and styles17 - `Wireframes` — low-fidelity layouts and flows18 - One page per major screen or user flow (e.g., `Dashboard`, `Settings`, `Onboarding`)193. **Define foundations first:**20 - Color palette (primary, secondary, neutral, semantic: success/warning/error/info)21 - Typography scale (headings h1-h6, body, caption, label, mono)22 - Spacing scale (aligned with Tailwind: 4px base unit)23 - Border radius tokens24 - Shadow/elevation tokens2526## Shape Creation2728### Creating shapes with full styling2930Use `penpot_page` tool actions. All shapes support `fills`, `opacity`, and `rotation` on creation.3132**Rectangles and frames** also support `r1`-`r4` for border radius on creation:3334```35penpot_page add-rectangle fileId=X pageId=Y x=0 y=0 width=200 height=10036 fills=[{fillColor: "#1E293B", fillOpacity: 1}]37 r1=12 r2=12 r3=12 r4=1238```3940**Text** supports `fontSize`, `fontWeight`, `fontFamily`, `fontColor` on creation:4142```43penpot_page add-text fileId=X pageId=Y x=0 y=0 width=200 height=4044 text="Hello World"45 fontSize="24" fontWeight="700" fontFamily="sourcesanspro" fontColor="#FFFFFF"46```4748### Available font families49- `sourcesanspro` — Source Sans Pro (default, always available)50- Custom fonts can be uploaded via `create-font-variant`5152### Font size and weight are STRINGS53Always pass `fontSize` and `fontWeight` as strings: `"24"` not `24`, `"700"` not `700`.5455## Shape Styling (modify-shape)5657After creation, use `modify-shape` to add advanced styling. First-class params:5859### Border Radius60```61penpot_page modify-shape fileId=X pageId=Y shapeId=Z r1=16 r2=16 r3=16 r4=1662```63- `r1` = top-left, `r2` = top-right, `r3` = bottom-right, `r4` = bottom-left6465### Shadows66```67penpot_page modify-shape fileId=X pageId=Y shapeId=Z68 shadow=[{69 style: "drop-shadow",70 color: {color: "#000000", opacity: 0.3},71 offsetX: 0, offsetY: 4, blur: 12, spread: 072 }]73```74- Styles: `drop-shadow`, `inner-shadow`75- UUID is auto-generated if not provided76- Multiple shadows supported (array)7778### Blur79```80penpot_page modify-shape fileId=X pageId=Y shapeId=Z81 blur={type: "layer-blur", value: 4}82```83- Types: `layer-blur`, `background-blur`84- UUID is auto-generated8586### Strokes87```88penpot_page modify-shape fileId=X pageId=Y shapeId=Z89 strokes=[{strokeColor: "#7C3AED", strokeOpacity: 0.5, strokeWidth: 2, strokeAlignment: "inner"}]90```91- Alignment: `inner`, `center`, `outer`92- Style (via attrs): `solid`, `dotted`, `dashed`, `mixed`, `none`9394### Text Content (re-style existing text)95```96penpot_page modify-shape fileId=X pageId=Y shapeId=Z97 textContent={98 type: "root",99 children: [{type: "paragraph-set", children: [{type: "paragraph", children: [{100 text: "Updated text",101 fontFamily: "sourcesanspro",102 fontSize: "32",103 fontWeight: "700",104 fontStyle: "normal",105 fillColor: "#FFFFFF",106 fillOpacity: 1107 }]}]}]108 }109```110111### Generic attrs (any shape attribute)112Use `attrs` as a catch-all for any shape property not covered above:113```114penpot_page modify-shape fileId=X pageId=Y shapeId=Z115 attrs={hidden: true, blocked: false}116```117118## Design Workflow — Best Practices119120### Step-by-step for polished designs1211221. **Create shapes** with basic geometry + fills + border radius1232. **Style shapes** with modify-shape: shadows, strokes, blur1243. **Create text** with proper fontSize/fontWeight/fontColor from the start1254. **Re-style text** with textContent on modify-shape if needed later1265. **Batch work** — create all shapes first, then style them (reduces API calls)127128### Performance tips129- Create shapes in rapid succession (each is one API call)130- Use modify-shape to batch multiple styling changes on one shape (fills + strokes + shadow in one call)131- Always get file features from `penpot get-file` before manual API calls132133### What works with the extension tools134| Feature | Create | Modify | Notes |135|---------|--------|--------|-------|136| Position & size | ✅ | ✅ | x, y, width, height |137| Fills | ✅ | ✅ | Array of fill objects |138| Border radius | ✅ | ✅ | r1, r2, r3, r4 params |139| Shadows | ❌ | ✅ | shadow param, UUID auto-generated |140| Blur | ❌ | ✅ | blur param, UUID auto-generated |141| Strokes | ✅ | ✅ | strokes param |142| Text styling | ✅ | ✅ | fontSize/fontWeight/fontColor on create; textContent on modify |143| Opacity | ✅ | ✅ | 0-1 number |144| Rotation | ✅ | ✅ | degrees |145146## Visual Review147148When you need to **review your own work** or the user asks you to **review a design**, capture actual rendered screenshots from Penpot's viewer.149150### When to do this151152- After creating or modifying designs — always screenshot to verify the result153- When the user says "review", "show me", "how does it look", "screenshot"154- Before handoff — capture final state for documentation155156### Workflow157158#### 1. Get the file and page IDs159160```161penpot get-file fileId=<fileId>162```163164#### 2. Create a share link165166```167penpot create-share-link fileId=<fileId> pages=[<pageId1>, <pageId2>] whoInspect="all"168```169170Share links are required — Penpot's viewer won't render without either authentication or a share token.171172#### 3. Screenshot each page with Playwright173174The Penpot viewer URL format is:175```176https://penpot.e9n.dev/#/view?file-id=<fileId>&page-id=<pageId>§ion=interactions&index=0&share-id=<shareId>177```178179Use Playwright to screenshot each page URL. Penpot is a complex ClojureScript SPA — wait 6+ seconds after navigation for it to render.180181> **Cleanup:** Always close the Playwright browser after capture to prevent leaking Chromium processes:182> ```javascript183> await browser.close();184> ```185186Save screenshots to `/tmp/penpot-<page-name>.png`, then `read` them to view inline. After viewing, delete the temporary file to avoid accumulating disk usage:187188```bash189rm /tmp/penpot-<page-name>.png190```191192#### 4. Review what you see193194After viewing the screenshot, evaluate:195196- **Layout** — is there wasted space? Are elements aligned to a grid?197- **Visual hierarchy** — can you immediately tell what's important?198- **Consistency** — do similar elements (cards, tags, buttons) share the same styling?199- **Completeness** — are there missing states, empty areas, placeholder content?200- **Accessibility** — text contrast, touch target sizes, focus indicators201- **Polish** — border radius consistency, shadow consistency, proper spacing202203## Component Creation204205Follow atomic design methodology:206207### Atoms (smallest building blocks)208- Buttons (primary, secondary, ghost, destructive — each with default/hover/active/disabled/focus)209- Inputs (text, textarea, select, checkbox, radio, toggle)210- Labels, badges, tags211- Icons (use Lucide as base set)212- Avatar, tooltip, separator213214### Molecules (composed atoms)215- Form fields (label + input + helper text + error message)216- Search bar (input + icon + button)217- Card (container + content slots)218- Menu item (icon + label + shortcut badge)219220### Organisms (composed molecules)221- Navigation (sidebar, topbar, breadcrumbs)222- Data tables (headers + rows + pagination + filters)223- Modals/dialogs (overlay + card + action buttons)224- Forms (multiple form fields + submit action)225226### Naming Convention227Use slash-separated categories: `category/component-name/variant`228- `buttons/primary/default`229- `buttons/primary/hover`230- `forms/text-input/filled`231- `navigation/sidebar/collapsed`232233## States & Variants234235Every interactive component needs these states:236- **Default** — resting state237- **Hover** — mouse over (desktop)238- **Active/Pressed** — being clicked/tapped239- **Focus** — keyboard focus (visible focus ring, WCAG required)240- **Disabled** — non-interactive241- **Loading** — async operation in progress (skeleton or spinner)242- **Error** — validation failure243244## Responsive Design245246Design at these breakpoints (aligned with Tailwind):247- **Mobile**: 375px (iPhone SE baseline)248- **Tablet**: 768px249- **Desktop**: 1280px250- **Wide**: 1536px251252Use Penpot's grid and layout features:253- Auto-layout for flex-like behavior254- Grid components for dashboard layouts255- Constraints for responsive positioning256257## Design-to-Code Handoff258259When preparing designs for developer handoff:2602611. **Export design tokens** as JSON:262 ```json263 {264 "colors": { "primary-500": "#7c6ff0", ... },265 "spacing": { "1": "4px", "2": "8px", ... },266 "typography": { "heading-1": { "size": "36px", "weight": 700, "lineHeight": 1.2 } },267 "radii": { "sm": "4px", "md": "8px", "lg": "12px" },268 "shadows": { "sm": "0 1px 2px rgba(0,0,0,0.05)", ... }269 }270 ```2712722. **Map components to shadcn-svelte** — document which Penpot component maps to which shadcn component and what customization is needed2732743. **Annotate specs** — add notes on each page for:275 - Spacing between elements276 - Interaction behavior (transitions, animations)277 - Edge cases (empty states, error states, loading states)278 - Accessibility notes (ARIA labels, focus order)2792804. **Export assets** — SVGs for icons and illustrations, optimized images281282## Penpot API Patterns283284Use the `pi-penpot` extension's three tools:285286- **`penpot`** — org-level: projects, files, teams, libraries, webhooks, snapshots, share links287- **`penpot_page`** — design: pages, shapes (create/modify/delete/move), components288- **`penpot_comment`** — collaboration: comment threads and replies289290### Critical API knowledge2912921. **Always get the file first** — `penpot get-file` returns pageIds and file features2932. **File features must match** — features from `get-file` must be passed back in `update-file`2943. **Transit+JSON encoding** — the extension handles this automatically for all write operations2954. **UUIDs auto-generated** — shadow and blur IDs are auto-generated when not provided2965. **camelCase in params, kebab-case internally** — the extension converts automatically297298## Checklist Before Handoff299300- [ ] All components use the design system (no one-off styles)301- [ ] States documented (default, hover, active, focus, disabled, error, loading)302- [ ] Responsive layouts at all breakpoints303- [ ] Color contrast passes WCAG AA (4.5:1 text, 3:1 large text/UI)304- [ ] Focus indicators visible on all interactive elements305- [ ] Empty states, error states, and loading states designed306- [ ] Design tokens exported as JSON307- [ ] Component-to-shadcn mapping documented308- [ ] Spacing and typography use the defined scale (no magic numbers)