skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
penpot/penpot-ai-kit91 installs

penpot-build-from-code

Translate an existing application page, view, or component code into a Penpot screen that is bound to the existing design system — mapping code styles onto semantic tokens and reusing library components instead of raw shapes. Use when the user has real code/markup and wants it reconstructed in Penpot, section by section, on-system. Triggers: 'build this in Penpot from code', 'turn this React/HTML/CSS into a Penpot screen', 'recreate this view in Penpot', 'port this page to Penpot', 'translate this component to Penpot bound to our tokens', 'code to Penpot'.

How do I install this agent skill?

npx skills add https://github.com/penpot/penpot-ai-kit --skill penpot-build-from-code
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubwarn

    The skill is designed for design-to-code translation but carries risks due to the dynamic assembly and execution of scripts using untrusted user input, as well as a surface for indirect prompt injection.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

Build From Code — Translate App Code into an On-System Penpot Screen

1. Title + How it works

This skill takes an existing application artifact — JSX/TSX, HTML, Vue/Svelte templates, CSS/Tailwind, or a styled-components tree — and reconstructs it as an editable, system-bound Penpot screen. 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). The core move is discover first (Phase 0 enumerates the existing tokens and components you must map onto), then transform the code incrementally, section by section, mapping each CSS/style value to a semantic token and each repeated UI part to an existing library component — never hardcoding hex or off-grid spacing, never reinventing a component as a raw box.

2. The One Rule That Matters Most

Never one-shot a screen, and never hardcode a value that a token already expresses. Build the screen one section at a time (one logical execute_code step per call), and before you write any fill/spacing/radius/type, resolve it against the discovered token set. A code-derived #0066FF is not a fill — it is a lookup into the design system. If no token matches, you propose one and stop; you do not invent it. Translating an entire page in a single call, or pasting raw computed CSS onto shapes, is the failure mode this skill exists to prevent.

3. Penpot MCP Tool Reference

Full surface: shared/penpot-mcp-tool-reference.md. The calls this skill leans on:

  • Discovery (read-only): penpotUtils.tokenOverview(), penpot.library.local (components, tokens), penpotUtils.shapeStructure(shape, depth), penpotUtils.findShape/findShapes.
  • Screen scaffold: penpot.createBoard(), board.addFlexLayout(), penpotUtils.addFlexLayout(container, dir).
  • Section build: penpot.createRectangle/createText/createEllipse/createPath, component.instance(), container.appendChild(child), child.layoutChild.
  • Token binding: shape.applyToken(token, properties[]), token.resolvedValue, penpotUtils.findTokenByName.
  • Validation: export_shape (separate tool call) and a shapeStructure read.

4. Plugin API Essentials

The gotchas that bite this skill, as one-liners — full text in shared/plugin-api-gotchas.md:

  • #1 Immutable style arrays — assign a brand-new fills/strokes array; never mutate fills[0].
  • #2 Token application is async (~100 ms) — apply in one execute_code call, verify resolvedValue in a later call.
  • #3 resize() forces text growType: 'fixed' — set growType = 'auto-width'/'auto-height' after resizing text you want to flow.
  • #4 Flex/grid overrides child x/y — position children by append order + gap/padding/align/justify; opt out only with child.layoutChild.absolute = true.
  • #5 width/height and parentX/parentY are read-only — use shape.resize(w, h) and penpotUtils.setParentXY(shape, x, y).
  • #6 Detach before mutating an instance's internals — detach() first and report it.
  • #7 Append or it isn't on the canvas — container.appendChild(child) (or penpot.currentPage.root).
  • #8 Padding tokens — padding tokens bind on ≥ 2.17; createScreenWrapper.js falls back to mirrored numbers + a ledger exception on older instances.
  • #9 switchVariant(pos, value) — pos is the index into variants.properties (see storage.run.ds.componentsByRole[role].axes), never the axis name.
  • #13b Exact font match — penpot.fonts.all.find(f => f.name === "…"); findByName is a substring search.
  • #15 layoutChild after append — set layoutChild.* only once the child is inside a layout board; openPage is async.
  • #16 flipX after fill sizing — run the clearFlip sweep after every section that uses fill sizing (shared/visual-effects.md).
  • #17 Layout geometry — await board.waitForLayoutUpdate() before reading geometry (replaces the sleep-100ms idiom).
  • Verify any unfamiliar signature with penpot_api_info(type, member) before using it.

5. Token-Aware Brief Contract

Before any mutation, restate the request as this contract. Act as a senior design-engineer who treats the design system as the single source of truth and never hardcodes a value when a token exists.

  • Context — what product/surface is this code from, who uses it, what page/view does it represent (e.g. "settings page of the B2B admin console, desktop").
  • Objective — single and specific: "rebuild the <SettingsPage> component as an editable Penpot board bound to the existing semantic token set and reusing the Button, InputField, and Card components."
  • Inputs — the actual code (component files, CSS/Tailwind config, design tokens export if any), the target Penpot page, the discovered token sets, the discovered component library.
  • Constraints — forbidden moves (no raw hex, no off-4px-grid spacing, no reinvented components, no detach() without approval), inviolable rules (semantic tokens not primitives; existing components over new shapes), viewport width.
  • Acceptance Criteria — quantitative: ≥95% of color/spacing/radius/type values bound to tokens (validated by validateScreen.js); zero orphan raw values flagged; every repeated UI part that has a matching library component is an instance, not a shape; all spacing on the 4px grid; layer names semantic (no Rectangle 12).

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)

Goal: map the existing Penpot design system so code values have somewhere to land. No mutation.

  1. high_level_overview (once).
  2. execute_code: run scripts/inspectDesignSystem.js → enumerate token sets, every token (name, type, resolvedValue), and every library component (name, id, variant axes). Cache into storage.run.ds.
  3. execute_code: penpotUtils.tokenOverview() to confirm the active theme/sets.
  4. Parse the input code into a section outline (header, nav, hero, form, card grid, footer…) and a style inventory (every distinct color/spacing/radius/font value the code uses). Exit criterion: you have (a) a token map keyed by resolved value, (b) a component catalog keyed by role, (c) a section outline, (d) a style inventory with each value pre-matched to a token or flagged UNMAPPED. ✋ Checkpoint: present the token/component coverage and the UNMAPPED list. Ask which UNMAPPED values should become proposed new tokens vs. accepted as documented exceptions. See references/01-discovery.md.

Phase 1 — Screen wrapper

Goal: create the screen Board with the right flex container and a RUN_ID ledger entry.

  1. execute_code: run scripts/createScreenWrapper.js (idempotent by board name) → Board sized to the viewport, addFlexLayout('column'), gap + padding bound to spacing tokens (padding mirrored + padding-mirrors-token exception on 2.16.x). Exit criterion: one empty, correctly-sized, flex column Board exists; its id is in the ledger. ✋ Checkpoint: export_shape the empty board; confirm dimensions/orientation before filling.

Phase 2 — Incremental section build (repeat per section)

Goal: build exactly one section from its code fragment, on-system. See references/02-component-assembly.md, references/03-token-binding.md, references/04-incremental-transform.md.

  1. execute_code: run scripts/buildSection.js for that one section — prefer component.instance() for any part with a matching catalog component; create raw shapes only for genuinely bespoke parts; bind every fill/spacing/radius/type to a token via applyToken.
  2. execute_code (separate call, after ~100 ms gap from the agent's turn): read back token.resolvedValue / shapeStructure to confirm bindings applied.
  3. export_shape the section. Exit criterion: the section renders, every value is token-bound or an approved exception, components are instances. ✋ Checkpoint: show the section; "looks good" approves only this section. Name the next section before continuing.

Phase 3 — Validation & report

Goal: prove the screen is on-system.

  1. execute_code: run scripts/validateScreen.js → token coverage %, list of orphan raw values, structure sanity (flex set, no auto-named layers, instances vs. shapes).
  2. Produce the structured report (section 12 of AGENTS.md). Exit criterion: acceptance criteria met or every gap explicitly listed for review. ✋ Checkpoint: present coverage + remaining gaps; ask for sign-off.

7. Critical Rules

  1. Always high_level_overview first; always Phase 0 discovery before any write.
  2. One section per execute_code mutation call. Never build multiple sections in one call.
  3. Every color/spacing/radius/border/type value MUST resolve to a token before it is applied. No raw hex/rgb/px on a shape when a matching token exists.
  4. All spacing/padding/gap MUST be a multiple of 4px (round code values to the nearest grid token; document any exception).
  5. Prefer semantic-tier tokens over primitives so theming works (tokens-schema.json).
  6. Any UI part with a matching library component MUST be an instance, not a hand-built shape group.
  7. Never invent a token. If a code value has no match, propose it (name/value/tier) and stop for approval.
  8. Token application is async — verify resolvedValue in a later execute_code call, never the same one.
  9. Never detach() an instance without explicit approval; report it if you do.
  10. Semantic, kebab-case layer names from the code's role (nav, header, card-container) — never Rectangle 12.
  11. Write the RUN_ID ledger after each phase; make every create idempotent (existence-check by name).

8. Domain Architecture

The skill produces a screen tree and consumes a design-system map.

  • Design-system map (Phase 0 output, cached in storage.run.ds):
    • tokensByValue: { "#0066FF": "color.action.primary.bg", "16": "spacing.4", ... } — reverse index for fast code-value → token lookup.
    • tokensByName: { "color.action.primary.bg": {type, resolvedValue} }.
    • componentsByRole: { button: {id, variants}, input: {...}, card: {...} }.
  • Section outline (from code): ordered list [{ role, codeFragment, childrenRoles }].
  • Style inventory: [{ codeValue, cssProp, matchedToken | "UNMAPPED" }].
  • Screen tree (Penpot output): Board(flex column) → section Boards(flex) → component instances + bound shapes.
  • Ledger (RUN_ID): { runId, phase, created:[{kind,name,id}], proposedTokens:[...], exceptions:[...], pendingReview:[...] }.

9. Modes & Policies

Default review (Apply-with-review): build each section, then stop at the checkpoint with an export_shape + summary before the next. Link: shared/modes-and-policies.md.

  • Safe-set (may auto-apply): semantic layer renaming; replacing a raw value that is exactly equal to a token's resolved value with that token (loss-less swap); adding plugin-data/metadata.
  • Always require review: creating the screen Board and any geometry; instantiating/restructuring components; any proposed new token; detach(); any token match that is a judgement call rather than an exact equality.

10. State Management

Link: shared/state-management.md. Ledger namespace penpot-ai, keyed by RUN_ID (e.g. bfc-2026-06-05-a).

  • storage.run.ds — cached design-system map (tokens, components) from Phase 0.
  • setSharedPluginData('penpot-ai', '<RUN_ID>.phase', n) — current phase.
  • setSharedPluginData('penpot-ai', '<RUN_ID>.ledger', JSON) — {boardId, sectionsBuilt:[role], proposedTokens, exceptions}.
  • Resume: re-read the ledger, re-run inspectDesignSystem.js to re-derive the DS map, shapeStructure(board) to see which sections exist, continue from ledger.phase. Idempotent steps make re-runs safe.

11. User Checkpoints

After phaseArtifacts shownWhat we ask
0 DiscoveryToken/component coverage table; UNMAPPED style values"Which UNMAPPED values become proposed tokens vs. documented exceptions?"
1 Wrapperexport_shape of empty Board + dimensions"Correct viewport size and column orientation?"
2 Section (each)export_shape of the section + token-binding summary"Approve THIS section? (next section is named explicitly)"
3 ValidationCoverage %, orphan-value list, structure sanity"Sign off, or address listed gaps first?"

12. Naming Conventions

Link: shared/naming-conventions.md. Domain specifics:

  • Screen Board: screen-<view-name> (kebab), e.g. screen-settings.
  • Section Boards: the semantic HTML role — header, nav, main, section, footer, or kebab role names like card-grid, field-row.
  • Component instances keep the catalog component name.
  • Proposed tokens follow dot-notation tiers from tokens-schema.json; prefer semantic names that describe intent derived from the code's usage (color.action.primary.bg, not color.blue.500).

13. Anti-Rationalization Table

Excuse the LLM makesWhy it's wrongDeterministic countermeasure that halts the flow
"The CSS says #0066FF, I'll just set that as the fill."Hardcoded hex is off-system; it won't theme and fails governance.Look it up in storage.run.ds.tokensByValue. If matched → applyToken. If not → propose a token and stop for approval. Never set raw hex.
"Tailwind p-5 is 20px, close enough to set 20."A literal bypasses the token scale and may be off-grid.Round to the nearest 4px-grid spacing token and applyToken. Document any exception only with approval.
"There's a Button component but it's faster to draw a rounded rect with text."Reinventing a system part breaks updates, variants, and handoff parity.If storage.run.ds.componentsByRole.button exists, component.instance() it and set props/variant. Raw shapes only for genuinely bespoke parts.
"I'll build the whole page in one execute_code to save calls."One-shotting hides failures and produces unbound, unverified output.Build one section per call; verify token bindings in a later call; export_shape and checkpoint before the next.
"This code value has no token; I'll add color.misc.x myself to keep moving."Silently inventing tokens pollutes the system and skips human governance.Add it to ledger.proposedTokens with name/value/tier and halt at the discovery checkpoint. Do not create it unilaterally.
"Token didn't read back; I'll just hardcode the value too as a fallback."A hardcoded fallback is exactly the orphan value this skill forbids; the read-back is async.Re-read resolvedValue in a later execute_code call. If it truly failed, fix the binding — never add a raw fallback.

14. Helper Code Snippets

// Reverse-index lookup: code value -> token (Phase 2, inside buildSection)
const ds = storage.run.ds;
const tokenName = ds.tokensByValue[String(codeValue).toLowerCase()]; // e.g. "#0066ff" -> "color.action.primary.bg"
if (tokenName) {
  const token = penpotUtils.findTokenByName(tokenName);
  shape.applyToken(token, ["fill"]);            // verify resolvedValue in a LATER call
} else {
  ledger.proposedTokens.push({ from: codeValue, suggest: "propose a semantic token", tier: "semantic" });
}
return { applied: !!tokenName, codeValue };
// Prefer an existing component over a raw shape
const role = "button";
const cat = storage.run.ds.componentsByRole[role];
let node;
if (cat) {
  const comp = penpot.library.local.components.find(c => c.id === cat.id);
  node = comp.instance();                         // instance, not a redrawn box
} else {
  node = penpot.createRectangle();                // bespoke only
}
container.appendChild(node);
return { role, usedComponent: !!cat, id: node.id };

15. Reference Resources

  • penpot_api_info("LibraryComponent"), penpot_api_info("Shape", "applyToken"), penpot_api_info("Board", "addFlexLayout"), penpot_api_info("Token", "resolvedValue") — verify before use.
  • shared/penpot-mcp-tool-reference.md (tool surface), shared/plugin-api-gotchas.md (traps), shared/tokens-schema.json (token types/tiers), shared/naming-conventions.md, shared/state-management.md.
  • Related skills: penpot-build-screen (brief-driven generate flow), penpot-foundations (when the system lacks tokens to map onto — its infer mode), penpot-audit-accessibility (post-build contrast check).

16. Supporting Files

references/

FileLoaded duringPurpose
references/01-discovery.mdPhase 0Enumerate tokens/components; build the reverse value→token index and component-by-role catalog; parse code into a section outline + style inventory.
references/02-component-assembly.mdPhase 2Decide instance-vs-shape; instantiate library components, set variants/props; assemble bespoke parts with flex.
references/03-token-binding.mdPhase 2Map code styles to semantic tokens; the applyToken property names; async verification; proposing new tokens.
references/04-incremental-transform.mdPhase 2The section-by-section loop, ordering, ledger updates, resumability.
references/05-anti-rationalization.mdAll phasesExpanded countermeasures with concrete code-translation traps.
references/06-error-recovery.mdOn failureDiagnosing failed bindings, orphan values, flex/x-y conflicts, detach mishaps.

scripts/

FilePhasePurpose
scripts/inspectDesignSystem.js0Enumerate token sets/tokens and library components; build the value→token reverse index + component-by-role catalog; cache in storage.
scripts/createScreenWrapper.js1Idempotently create the screen Board with flex column and token-bound padding/gap.
scripts/buildSection.js2Build one section from a code fragment: instance components, create bespoke shapes, bind tokens.
scripts/validateScreen.js3Compute token coverage, list orphan raw values, check structure sanity (flex, names, instances).

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.

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-build-from-code">View penpot-build-from-code on skillZs</a>