# Penpot Workflow

> 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.

- Skill: `espennilsen/penpot-workflow` (Agent Skill)
- Install (CLI): `npx skillmds@latest add espennilsen/penpot-workflow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/espennilsen/penpot-workflow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: espennilsen (https://skillmd.com/u/espennilsen)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/espennilsen/penpot-workflow

---


# 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:

1. **Create the Penpot project** with a clear, matching name (same as the code repo)
2. **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`)
3. **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

1. **Create shapes** with basic geometry + fills + border radius
2. **Style shapes** with modify-shape: shadows, strokes, blur
3. **Create text** with proper fontSize/fontWeight/fontColor from the start
4. **Re-style text** with textContent on modify-shape if needed later
5. **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>&section=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:
> ```javascript
> 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:

```bash
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:

1. **Export design tokens** as JSON:
   ```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)", ... }
   }
   ```

2. **Map components to shadcn-svelte** — document which Penpot component maps to which shadcn component and what customization is needed

3. **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)

4. **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

1. **Always get the file first** — `penpot get-file` returns pageIds and file features
2. **File features must match** — features from `get-file` must be passed back in `update-file`
3. **Transit+JSON encoding** — the extension handles this automatically for all write operations
4. **UUIDs auto-generated** — shadow and blur IDs are auto-generated when not provided
5. **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)

