penpot-document-handoff
Document a Penpot design for handoff with a hideable annotation layer BESIDE the design (never on top of it): a context card (goal, business rules, links, status), numbered pins on the UI, matching observation/recommendation notes, optional tooltips. Reuses or proposes an annotation component kit + tokens. NOT for auditing (penpot-audit-*) or renaming layers (penpot-rename-layers). Triggers: 'document this design', 'annotate this screen', 'prepare this for handoff', 'add observation notes', 'create a critique card', 'spec this screen for handoff'.
How do I install this agent skill?
npx skills add https://github.com/penpot/penpot-ai-kit --skill penpot-document-handoffIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill automates the process of creating documentation and handoff annotations for Penpot designs. It follows a structured workflow with mandatory checkpoints and an automated audit to ensure the original design remains untouched. No malicious code, data exfiltration, or unauthorized remote access patterns were found.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
penpot-document-handoff — design → annotated handoff
1. Title + How it works
penpot-document-handoff turns a finished (or in-progress) design into a self-explaining handoff:
a context card to its left (the "How might we" goal, business rules, links, designer(s), status,
feedback wanted / not wanted), numbered pins on the UI regions, matching observation /
recommendation note cards to its right, and optional tooltips for transient states — every
annotation living in one hideable group so reviewers can toggle back to the clean design with one
click. It reuses an existing annotation component kit + annotation.* tokens when present and proposes
a minimal one (human-approved) when missing. Every mutation goes through execute_code;
validate visually with export_shape; read structure with penpotUtils.shapeStructure (full tool
surface: shared/penpot-mcp-tool-reference.md). It first discovers the target
design + any existing annotation component kit and tokens, then builds the annotation layer
incrementally — one annotation per call — strictly as a sibling layer, never editing the
design itself.
2. The One Rule That Matters Most
Annotate beside, never on top — and one annotation per call. The design being documented is
read-only: you add a separate, hideable annotation layer next to it and you never move, restyle, or
restructure a single shape of the original. Build the context card, then add pins+notes one at a time,
checkpointing as you go. Generating the whole annotation set in one execute_code call is a failure
mode.
3. Penpot MCP Tool Reference
Full surface: shared/penpot-mcp-tool-reference.md. Key calls this skill leans on:
penpotUtils.shapeStructure / findShape / findShapes (locate the design + kit components);
penpot.library.local.components + comp.instance() (reuse the annotation kit); penpot.createBoard()
/ addFlexLayout() (build the kit if missing); penpot.group() (the hideable wrapper — verify with
penpot_api_info); shape.applyToken (bind accent/surface/type); export_shape('selection'|<id>) at
every checkpoint.
4. Plugin API Essentials
Gotcha numbers refer to shared/plugin-api-gotchas.md.
- The design is read-only input. Read it with
shapeStructure; resolve pin anchor points from childbounds(absolute x/y/width/height). Never callresize/reparent/detach/removeon it. - Annotation cards are Boards with flex (
column-reverse, matching the reference system); compose with append order + gaps + padding, never absolute x/y inside a card. - Pins and the context card sit at absolute page positions (they are not inside the design's
layout) — set
x/ydirectly, orlayoutChild.absolute = trueif parented into a layout board (#14: absolute children use PAGE coordinates, and boards clip at their edges). - #3 text auto-sizing — set
growTypetoauto-width/auto-heightafter anyresize()(which forcesfixed); sleep ~100 ms before reading backtextBounds. - #2 async token application — apply in one call, verify
resolvedValuein a LATER call. - #11 every new Board is born with an OPAQUE WHITE fill — this skill's critical failure mode.
createBoard()shipsfills = [{ fillColor: "#FFFFFF", fillOpacity: 1 }]. Left in place, a structural wrapper's square white corners poke out behind the rounded card it wraps, and any board keeping the literal white is off-system (never themes). Fill policy: note/context cards and tooltips are genuine surfaces → bind their bg tocolor.annotation.surface(never a literal); inner section boards and wrappers are structural → clear withboard.fills = []. - #13b exact font match —
penpot.fonts.all.find(f => f.name === "Work Sans") || nullwith a null-guard;findByNameis a substring search. If the font is absent, keep the default and reportfont-fallback. - Wrap-to-hide: collect every annotation shape and
group()them under one named group; hiding =group.hidden = true. Verifygroup()/ grouping signature withpenpot_api_infobefore relying on it.
5. Token-Aware Brief Contract
Restate the request before mutating. Act as a senior design-systems / handoff specialist who never annotates on top of the work and never hardcodes the annotation palette.
- Context — product, audience, the feature/flow being documented, target reader (devs? PMs? design critique?).
- Objective — the single design (board/frame or selection) to document and what the handoff must make unambiguous.
- Inputs — the design board id; the handoff brief content (US number, title, the "How might we" goal, context/business rules, links, designer(s), status, platform, feedback wanted / not wanted); the list of UI regions to pin with their observation (and optional recommendation) text; any transient states needing tooltips.
- Constraints — do not modify the design; reuse the annotation kit if present (else propose one);
bind accent/surface/type to tokens (propose
annotation.*tokens if missing — human approves); pins numbered contiguously and matched 1:1 to notes; everything inside the hideable group. - Acceptance Criteria — design byte-for-byte untouched; every pin has exactly one matching note and vice-versa; numbering contiguous from 1; all annotation text bound to annotation type tokens; accent & surface bound to tokens; the whole layer hides/shows from one group toggle; 3-column reading order (context | design | notes) holds.
6. Mandatory Workflow
Visual self-review (mandatory): before every ✋ checkpoint that shows visual work, run the export → look → fix loop from
shared/visual-self-review.md— export the unit you just built, inspect the image yourself against the checklist, fix visible defects (max 2 iterations), and present that same export with any remaining defects named.
Phase 0 — Discovery (read-only). high_level_overview; resolve the target design
(penpot.selection or a named board) and copy its id into storage.dh; read its structure
(scripts/locateDesign pattern, depth 2–3) to enumerate pinnable regions; inventory the annotation kit
- tokens (
scripts/setupAnnotationKit.jsindryRun). ✋ Checkpoint: confirm the design + whether a kit exists or must be created.
Phase 1 — Handoff brief. Collect/confirm the context-card content and the pin list (region →
observation → optional recommendation). Use references/02-handoff-brief.md / prompts/handoff-brief.md.
✋ Checkpoint: approve the brief + pin list before any drawing.
Phase 2 — Ensure the annotation kit. If the kit components + annotation.* tokens exist, reuse
them. If not, propose the minimal kit (Critique Card, Chip Note, Pointer S/L, Tooltip) + tokens
(color.annotation.accent, color.annotation.surface, color.annotation.on-accent,
color.annotation.body, font.annotation.title|body|label) per references/04-component-and-tokens.md,
and create it only after approval (scripts/setupAnnotationKit.js). ✋ Checkpoint: approve kit +
token names/values/tier.
Phase 3 — Context card. Build/instance the Critique Card, fill it from the brief, place it to the
left of the design (scripts/buildContextCard.js). ✋ Checkpoint: export_shape the card.
Phase 4 — Pins + notes (one per call). For each region: place a numbered Pointer/L pin anchored to
the region on the design, and a matching Chip Note in the right column with the same number
(scripts/placePinAndNote.js). Optionally add a Tooltip for a transient state
(scripts/addTooltipCallout.js). One annotation per execute_code call. ✋ Checkpoint:
export_shape after each (or each small batch).
Phase 5 — Wrap & audit. Group every annotation shape under one hideable group named
Hide this to quit notes; run the self-audit (scripts/finalizeHandoff.js): design untouched, pin↔note
parity, contiguous numbering, tokens bound, 3-column order. A non-empty violations fails the gate —
fix before reporting done. Report.
7. Critical Rules
- Never modify the design. No geometry, fill, name, or structure change on any original shape. The skill only adds a sibling annotation layer. The Phase 5 audit fails the run if the design's shape set or bounds changed.
- One annotation per
execute_codecall. Context card, then each pin+note individually. Never one-shot the whole layer. - Pin↔note parity & contiguous numbering. Every Pointer pin has exactly one Chip Note with the same number; numbers run 1..N with no gaps; the on-canvas pin (L) and the note's header pin (S) share the number.
- Reuse the kit; propose tokens, never hardcode. Instance existing annotation components; if the
kit/tokens are missing, propose and create them under review — never paint raw
#1A88E0/#E4F3FF. - 3-column layout. Context card left, design center (untouched), note cards stacked right; pins overlay the regions they point to.
- Flex inside every card. Cards/sections are flex Boards (
column-reverselike the reference), not absolute-positioned text or plain Groups. - Everything hideable from one group. All annotation shapes live under a single named group so reviewers toggle the clean design with one click.
- Default mode = review. Geometry is created → checkpoint at each phase; kit/token creation is a never-autofix change requiring approval.
8. Domain Architecture
The annotation layer is three columns around the untouched design:
[ Critique Card ] [ the design (read-only) ] [ Chip Note #1 ]
#US · title ┌─────────┐ number+TITLE
State/Copy/Platform │ ① ② ③ │ ← Pointer/L pins Observation
status pill │ UI… │ on the regions Recommendation?
HMW goal └─────────┘ [ Chip Note #2 ] …
links · designers (+ Tooltip/Top|Bottom for
context transient states)
feedback wanted
feedback NOT wanted
All of it (cards + pins + tooltips) is wrapped in one group: Hide this to quit notes.
Component kit + token taxonomy: references/01-annotation-anatomy.md, references/04-component-and-tokens.md.
9. Modes & Policies
Default review (shared/modes-and-policies.md). Creating geometry, components, or tokens always
requires a checkpoint. Safe-set items this skill may auto-apply: adding documentation metadata
(plugin-data tags) and renaming an auto-named annotation layer it just created. Never auto-fix:
creating the kit components, creating annotation.* tokens, and (always) any change to the design being
documented. Fill policy: note/context cards are genuine surfaces → bind their bg to
color.annotation.surface; inner section boards are structural → fills = [].
10. State Management
Ledger under RUN_ID (shared/state-management.md), in storage.dh + an in-file breadcrumb:
phase, designBoardId, kit:{critiqueCardId,chipNoteId,pointerSId,pointerLId,tooltipId,created},
tokens:{accent,surface,...}, contextCardId, notesColumnX, groupId,
pins:[{n,regionId,pinId,noteId}], pinCounter. On resume: re-read the ledger, re-derive reality with
a structure read (the group + pins), and continue from pinCounter.
11. User Checkpoints
| After phase | Artifacts shown | What we ask |
|---|---|---|
| 0 discovery | design summary + pinnable regions + kit/token presence | Confirm target + kit plan? |
| 1 brief | context-card content + numbered pin list | Approve brief + pins? |
| 2 kit | proposed components + token names/values/tier (only if creating) | Approve kit + tokens? |
| 3 context card | export_shape of the card | Approve content/placement? |
| each pin+note | export_shape of the region + note | Approve / adjust? |
| 5 wrap & audit | hideable group + audit result | Approve handoff / iterate? |
12. Naming Conventions
shared/naming-conventions.md. Components PascalCase (Critique Card, Chip Note, Pointer,
Tooltip — keep the reference system's names if reusing). Tokens dot-notation under an annotation
set (color.annotation.accent, font.annotation.label). Layers kebab-case/semantic
(context-card, note-card, pin, annotations); the hideable group keeps the recognizable name
Hide this to quit notes. Pin labels are the integer index.
13. Anti-Rationalization Table
| Excuse the LLM makes | Why it's wrong | Deterministic countermeasure that halts the flow |
|---|---|---|
| "I'll just drop the note text on top of the design / recolor a layer to highlight it." | That mutates the deliverable; handoff annotations must be removable and non-destructive. | Stop. Add a sibling annotation shape in the notes column or a Pointer pin overlay; never edit an original shape. The Phase 5 audit fails if the design changed. |
"I'll create all the pins and notes in one execute_code to save calls." | One-shot output is unauditable and de-syncs pin↔note numbering. | One annotation per call; checkpoint with export_shape; advance storage.dh.pinCounter each time. |
"The accent blue is just decoration — hardcode #1A88E0." | Off-system values fail governance and won't theme/rebrand. | Bind to color.annotation.accent; if it doesn't exist, propose it (name/value/tier) for approval. |
| "This note doesn't really need a pin — it's general." | Orphan notes break the number→region reading model reviewers rely on. | Every Chip Note gets a Pointer pin on a concrete region; if it's truly global, it belongs in the context card, not a note. |
| "A plain text block is faster than instancing the Chip Note component." | Raw text drifts from the system and won't restyle with the kit. | Instance the kit component; if the kit is missing, create it once (under review) and reuse. |
| "I'll renumber notes later; order doesn't matter yet." | Gaps/dupes in numbering silently corrupt the handoff. | Keep numbering contiguous and matched at every step; the audit rejects gaps/dupes. |
| "I'll skip the hideable group; the layers are tidy enough." | Without one toggle, reviewers can't get back to the clean design. | Wrap all annotation shapes under the single Hide this to quit notes group before reporting done. |
14. Helper Code Snippets
// Phase 0 — lock the target design + enumerate pinnable regions (READ-ONLY)
const design = penpot.selection[0] || penpotUtils.findShape(s => s.name === "REPLACE-ME-board");
if (!design) return { error: "Select the design board to document, or pass its name." };
storage.dh = Object.assign(storage.dh || {}, { designBoardId: design.id });
const regions = (design.children || []).map(c => ({
id: c.id, name: c.name, type: c.type,
x: Math.round(c.x), y: Math.round(c.y), w: Math.round(c.width), h: Math.round(c.height)
}));
return { design: { id: design.id, name: design.name, x: Math.round(design.x), y: Math.round(design.y),
w: Math.round(design.width), h: Math.round(design.height) }, regionCount: regions.length, regions };
// Reference palette/typography of the annotation system (verified from the example file).
// Use these as the VALUES when proposing annotation.* tokens — do NOT paint them raw on shapes.
return {
color: { accent: "#1A88E0", surface: "#E4F3FF", onAccent: "#FFFFFF", body: "#000000" },
type: { family: "Work Sans",
label: { size: 12, weight: 500, case: "uppercase", fill: "accent" },
body: { size: 14, weight: 400, fill: "body" },
title: { size: 24, weight: 400, fill: "body" } },
card: { bg: "surface", padding: [24,12,24,12], flexDir: "column-reverse", sectionGap: 24, labelGap: 4 },
pin: { L: 40, S: 24, radius: 9999, bg: "accent", number: "onAccent" }
};
15. Reference Resources
penpot_api_info('Board'),penpot_api_info('LibraryComponent'),penpot_api_info('Text'),penpot_api_info('Context','group')— verify the grouping signature before wrapping.shared/penpot-mcp-tool-reference.md(tool surface),shared/plugin-api-gotchas.md(#11 fill policy; async tokens; resize→growType),shared/tokens-schema.json(tokentypespellings).
16. Supporting Files
references/ (progressive-disclosure deep dives)
| File | Loaded when | Contents |
|---|---|---|
references/01-annotation-anatomy.md | Phases 2–4 | The reverse-engineered system: Critique Card sections, Chip Note structure, Pointer S/L, Tooltip, palette/type, the hideable group. |
references/02-handoff-brief.md | Phase 1 | How to elicit/structure the context-card content and the pin list. |
references/03-layout-placement.md | Phases 3–4 | 3-column geometry, pin anchoring math, note stacking, spacing rhythm. |
references/04-component-and-tokens.md | Phase 2 | Find-or-create the annotation component kit + the annotation.* token proposals. |
references/05-anti-rationalization.md | any phase | Extended failure-mode catalogue with countermeasures. |
references/06-error-recovery.md | on failure | Recovery for missing kit, broken numbering, accidental design edits, group/ungroup issues. |
scripts/ (paste-into-execute_code templates)
| File | Phase | Purpose |
|---|---|---|
scripts/setupAnnotationKit.js | 0/2 | Inventory the kit + tokens; in non-dryRun, create the minimal kit + propose tokens. |
scripts/buildContextCard.js | 3 | Build/instance the Critique Card and fill it from the brief. |
scripts/placePinAndNote.js | 4 | Add ONE numbered pin on a region + its matching Chip Note. |
scripts/addTooltipCallout.js | 4 | Add an optional Tooltip callout for a transient/hover state. |
scripts/finalizeHandoff.js | 5 | Wrap annotations in the hideable group and run the parity/untouched/token audit. |
Doctrine paths. shared/… and policies/… resolve inside this bundle in native installs (vendored by the installer); in a Claude Code plugin install they live at the plugin root — ${CLAUDE_PLUGIN_ROOT}/shared/…, two directories up from this file.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/penpot/penpot-ai-kit/penpot-document-handoff">View penpot-document-handoff on skillZs</a>