Skip to content
Back to skills

Penpot Document Handoff

ASecurity

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

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
ai-agentsgoapidocumentation

Works with

  • claude code
  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 12 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add huytran1120/vietnam-agentic-skills --skill penpot-document-handoff --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Penpot Document Handoff?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Penpot Document Handoff
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/huytran1120-penpot-document-handoff/badge)](https://www.skillsdirectory.com/skills/huytran1120-penpot-document-handoff)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: penpot-document-handoff
description: "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'."
disable-model-invocation: false
version: 0.2.1
audiences: [product-designer, design-engineer, design-system]
mode-default: review
requires:
  - shared/penpot-mcp-tool-reference.md
  - shared/plugin-api-gotchas.md
  - shared/tokens-schema.json
  - shared/naming-conventions.md
  - shared/state-management.md
  - shared/modes-and-policies.md
  - shared/visual-self-review.md
---

# 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
  child `bounds` (absolute x/y/width/height). Never call `resize`/reparent/`detach`/`remove` on 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`/`y` directly, or `layoutChild.absolute = true` if parented into a layout board
  (**#14**: absolute children use PAGE coordinates, and boards clip at their edges).
- **#3 text auto-sizing** — set `growType` to `auto-width`/`auto-height` after any `resize()` (which
  forces `fixed`); sleep ~100 ms before reading back `textBounds`.
- **#2 async token application** — apply in one call, verify `resolvedValue` in a LATER call.
- **#11 every new Board is born with an OPAQUE WHITE fill — this skill's critical failure mode.**
  `createBoard()` ships `fills = [{ 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 to `color.annotation.surface` (never a literal);
  inner section boards and wrappers are **structural** → clear with `board.fills = []`.
- **#13b exact font match** — `penpot.fonts.all.find(f => f.name === "Work Sans") || null` with a null-guard;
  `findByName` is a substring search. If the font is absent, keep the default and report `font-fallback`.
- Wrap-to-hide: collect every annotation shape and `group()` them under one named group; hiding =
  `group.hidden = true`. Verify `group()` / grouping signature with `penpot_api_info` before 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.js` in `dryRun`). **✋ 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
1. **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.
2. **One annotation per `execute_code` call.** Context card, then each pin+note individually. Never
   one-shot the whole layer.
3. **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.
4. **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`.
5. **3-column layout.** Context card left, design center (untouched), note cards stacked right; pins
   overlay the regions they point to.
6. **Flex inside every card.** Cards/sections are flex Boards (`column-reverse` like the reference), not
   absolute-positioned text or plain Groups.
7. **Everything hideable from one group.** All annotation shapes live under a single named group so
   reviewers toggle the clean design with one click.
8. **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
```js
// 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 };
```
```js
// 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` (token `type` spellings).

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

Files in this skill

  • SKILL.md18.9 KB
  • references/01-annotation-anatomy.md5 KB
  • references/02-handoff-brief.md3.4 KB
  • references/03-layout-placement.md2.8 KB
  • references/04-component-and-tokens.md5.3 KB
  • references/05-anti-rationalization.md2.5 KB
  • references/06-error-recovery.md2.6 KB
  • scripts/addTooltipCallout.js2.8 KB
  • scripts/buildContextCard.js5.5 KB
  • scripts/finalizeHandoff.js4.5 KB
  • scripts/placePinAndNote.js7.8 KB
  • scripts/setupAnnotationKit.js5.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.