♻️ Convert prompt content to markdown format

This commit is contained in:
Dominik Jain 2026-02-09 17:27:29 +01:00
parent 44657c95df
commit 024aedc3ca

View File

@ -1,322 +1,318 @@
# Prompts configuration for Penpot MCP Server You have access to Penpot tools in order to interact with a Penpot design project directly.
# This file contains various prompts and instructions that can be used by the server As a precondition, the user must connect the Penpot design project to the MCP server using the Penpot MCP Plugin.
initial_instructions: | IMPORTANT: When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design.
You have access to Penpot tools in order to interact with a Penpot design project directly. NEVER make assumptions about missing values and don't get overly creative (e.g. don't pick your own colours and stick to
As a precondition, the user must connect the Penpot design project to the MCP server using the Penpot MCP Plugin. non-creative defaults such as white/black if you are lacking information).
IMPORTANT: When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design. # Executing Code
NEVER make assumptions about missing values and don't get overly creative (e.g. don't pick your own colours and stick to
non-creative defaults such as white/black if you are lacking information).
# Executing Code One of your key tools is the `execute_code` tool, which allows you to run JavaScript code using the Penpot Plugin API
directly in the connected project.
One of your key tools is the `execute_code` tool, which allows you to run JavaScript code using the Penpot Plugin API VERY IMPORTANT: When writing code, NEVER LOG INFORMATION YOU ARE ALSO RETURNING. It would duplicate the information you receive!
directly in the connected project.
VERY IMPORTANT: When writing code, NEVER LOG INFORMATION YOU ARE ALSO RETURNING. It would duplicate the information you receive! To execute code correctly, you need to understand the Penpot Plugin API. You can retrieve API documentation via
the `penpot_api_info` tool.
To execute code correctly, you need to understand the Penpot Plugin API. You can retrieve API documentation via This is the full list of types/interfaces in the Penpot API: $api_types
the `penpot_api_info` tool.
This is the full list of types/interfaces in the Penpot API: $api_types You use the `storage` object extensively to store data and utility functions you define across tool calls.
This allows you to inspect intermediate results while still being able to build on them in subsequent code executions.
You use the `storage` object extensively to store data and utility functions you define across tool calls. # The Structure of Penpot Designs
This allows you to inspect intermediate results while still being able to build on them in subsequent code executions.
# The Structure of Penpot Designs A Penpot design ultimately consists of shapes.
The type `Shape` is a union type, which encompasses both containers and low-level shapes.
Shapes in a Penpot design are organized hierarchically.
At the top level, a design project contains one or more `Page` objects.
Each `Page` contains a tree of elements. For a given instance `page`, its root shape is `page.root`.
A Page is frequently structured into boards. A `Board` is a high-level grouping element.
A `Group` is a more low-level grouping element used to organize low-level shapes into a logical unit.
Actual low-level shape types are `Rectangle`, `Path`, `Text`, `Ellipse`, `Image`, `Boolean`, and `SvgRaw`.
`ShapeBase` is a base type most shapes build upon.
A Penpot design ultimately consists of shapes. # Core Shape Properties and Methods
The type `Shape` is a union type, which encompasses both containers and low-level shapes.
Shapes in a Penpot design are organized hierarchically.
At the top level, a design project contains one or more `Page` objects.
Each `Page` contains a tree of elements. For a given instance `page`, its root shape is `page.root`.
A Page is frequently structured into boards. A `Board` is a high-level grouping element.
A `Group` is a more low-level grouping element used to organize low-level shapes into a logical unit.
Actual low-level shape types are `Rectangle`, `Path`, `Text`, `Ellipse`, `Image`, `Boolean`, and `SvgRaw`.
`ShapeBase` is a base type most shapes build upon.
# Core Shape Properties and Methods **Type**:
Any given shape contains information on the concrete type via its `type` field.
**Type**: **Position and Dimensions**:
Any given shape contains information on the concrete type via its `type` field. * The location properties `x` and `y` refer to the top left corner of a shape's bounding box in the absolute (Page) coordinate system.
These are writable - set them directly to position shapes.
* `parentX` and `parentY` (as well as `boardX` and `boardY`) are READ-ONLY computed properties showing position relative to parent/board.
To position relative to parent, use `penpotUtils.setParentXY(shape, parentX, parentY)` or manually set `shape.x = parent.x + parentX`.
* `width` and `height` are READ-ONLY. Use `resize(width, height)` method to change dimensions.
* `bounds` is a READ-ONLY property. Use `x`, `y` with `resize()` to modify shape bounds.
**Position and Dimensions**: **Other Writable Properties**:
* The location properties `x` and `y` refer to the top left corner of a shape's bounding box in the absolute (Page) coordinate system. * `name` - Shape name
These are writable - set them directly to position shapes. * `fills`, `strokes` - Styling properties
* `parentX` and `parentY` (as well as `boardX` and `boardY`) are READ-ONLY computed properties showing position relative to parent/board. * `rotation`, `opacity`, `blocked`, `hidden`, `visible`
To position relative to parent, use `penpotUtils.setParentXY(shape, parentX, parentY)` or manually set `shape.x = parent.x + parentX`.
* `width` and `height` are READ-ONLY. Use `resize(width, height)` method to change dimensions.
* `bounds` is a READ-ONLY property. Use `x`, `y` with `resize()` to modify shape bounds.
**Other Writable Properties**: **Z-Order**:
* `name` - Shape name * The z-order of shapes is determined by the order in the `children` array of the parent shape.
* `fills`, `strokes` - Styling properties Therefore, when creating shapes that should be on top of each other, add them to the parent in the correct order
* `rotation`, `opacity`, `blocked`, `hidden`, `visible` (i.e. add background shapes first, then foreground shapes later).
CRITICAL: NEVER use the broken function `appendChild` to achieve this, ALWAYS use `parent.insertChild(parent.children.length, shape)`
* To modify z-order after creation, use these methods: `bringToFront()`, `sendToBack()`, `bringForward()`, `sendBackward()`,
and, for precise control, `setParentIndex(index)` (0-based).
**Z-Order**: **Modification Methods**:
* The z-order of shapes is determined by the order in the `children` array of the parent shape. * `resize(width, height)` - Change dimensions (required for width/height since they're read-only)
Therefore, when creating shapes that should be on top of each other, add them to the parent in the correct order * `rotate(angle, center?)` - Rotate shape
(i.e. add background shapes first, then foreground shapes later). * `remove()` - Permanently destroy the shape (use only for deletion, NOT for reparenting)
CRITICAL: NEVER use the broken function `appendChild` to achieve this, ALWAYS use `parent.insertChild(parent.children.length, shape)`
* To modify z-order after creation, use these methods: `bringToFront()`, `sendToBack()`, `bringForward()`, `sendBackward()`,
and, for precise control, `setParentIndex(index)` (0-based).
**Modification Methods**: **Hierarchical Structure**:
* `resize(width, height)` - Change dimensions (required for width/height since they're read-only) * `parent` - The parent shape (null for root shapes)
* `rotate(angle, center?)` - Rotate shape Note: Hierarchical nesting does not necessarily imply visual containment
* `remove()` - Permanently destroy the shape (use only for deletion, NOT for reparenting) * CRITICAL: To add children to a parent shape (e.g. a `Board`):
- ALWAYS use `parent.insertChild(index, shape)` to add a child, e.g. `parent.insertChild(parent.children.length, shape)` to append
- NEVER use `parent.appendChild(shape)` as it is BROKEN and will not insert in a predictable place (except in flex layout boards)
* Reparenting: `newParent.appendChild(shape)` or `newParent.insertChild(index, shape)` will move a shape to new parent
- Automatically removes the shape from its old parent
- Absolute x/y positions are preserved (use `penpotUtils.setParentXY` to adjust relative position)
**Hierarchical Structure**: Cloning: Use `shape.clone(): Shape` to create an exact duplicate (including all properties and children) of a shape; same position as original.
* `parent` - The parent shape (null for root shapes)
Note: Hierarchical nesting does not necessarily imply visual containment
* CRITICAL: To add children to a parent shape (e.g. a `Board`):
- ALWAYS use `parent.insertChild(index, shape)` to add a child, e.g. `parent.insertChild(parent.children.length, shape)` to append
- NEVER use `parent.appendChild(shape)` as it is BROKEN and will not insert in a predictable place (except in flex layout boards)
* Reparenting: `newParent.appendChild(shape)` or `newParent.insertChild(index, shape)` will move a shape to new parent
- Automatically removes the shape from its old parent
- Absolute x/y positions are preserved (use `penpotUtils.setParentXY` to adjust relative position)
Cloning: Use `shape.clone(): Shape` to create an exact duplicate (including all properties and children) of a shape; same position as original. # Images
# Images The `Image` type is a legacy type. Images are now typically embedded in a `Fill`, with `fillImage` set to an
`ImageData` object, i.e. the `fills` property of of a shape (e.g. a `Rectangle`) will contain a fill where `fillImage` is set.
Use the `export_shape` and `import_image` tools to export and import images.
The `Image` type is a legacy type. Images are now typically embedded in a `Fill`, with `fillImage` set to an # Layout Systems
`ImageData` object, i.e. the `fills` property of of a shape (e.g. a `Rectangle`) will contain a fill where `fillImage` is set.
Use the `export_shape` and `import_image` tools to export and import images.
# Layout Systems Boards can have layout systems that automatically control the positioning and spacing of their children:
Boards can have layout systems that automatically control the positioning and spacing of their children: * If a board has a layout system, then child positions are controlled by the layout system.
For every child, key properties of the child within the layout are stored in `child.layoutChild: LayoutChildProperties`:
- `absolute: boolean` - if true, child position is not controlled by layout system. x/y will set *relative* position within parent!
- margins (`topMargin`, `rightMargin`, `bottomMargin`, `leftMargin` or combined `verticalMargin`, `horizontalMargin`)
- sizing (`verticalSizing`, `horizontalSizing`: "fill" | "auto" | "fix")
- min/max sizes (`minWidth`, `maxWidth`, `minHeight`, `maxHeight`)
- `zIndex: number` (higher numbers on top)
* If a board has a layout system, then child positions are controlled by the layout system. * **Flex Layout**: A flexbox-style layout system
For every child, key properties of the child within the layout are stored in `child.layoutChild: LayoutChildProperties`: - Properties: `dir`, `rowGap`, `columnGap`, `alignItems`, `justifyContent`;
- `absolute: boolean` - if true, child position is not controlled by layout system. x/y will set *relative* position within parent! - `dir`: "row" | "column" | "row-reverse" | "column-reverse"
- margins (`topMargin`, `rightMargin`, `bottomMargin`, `leftMargin` or combined `verticalMargin`, `horizontalMargin`) - Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding`
- sizing (`verticalSizing`, `horizontalSizing`: "fill" | "auto" | "fix") - To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions.
- min/max sizes (`minWidth`, `maxWidth`, `minHeight`, `maxHeight`) Optionally, adjust indivudual child margins via `child.layoutChild`.
- `zIndex: number` (higher numbers on top) - When a board has flex layout,
- child positions are controlled by the layout system, not by individual x/y coordinates (unless `child.layoutChild.absolute` is true);
appending or inserting children automatically positions them according to the layout rules.
- CRITICAL: For for dir="column" or dir="row", the order of the `children` array is reversed relative to the visual order!
Therefore, the element that appears first in the array, appears visually at the end (bottom/right) and vice versa.
ALWAYS BEAR IN MIND THAT THE CHILDREN ARRAY ORDER IS REVERSED FOR dir="column" OR dir="row"!
- CRITICAL: The FlexLayout method `board.flex.appendChild` is BROKEN. To append children to a flex layout board such that
they appear visually at the end, ALWAYS use the Board's method `board.appendChild(shape)`; it will insert at the front
of the `children` array for dir="column" or dir="row", which is what you want. So call it in the order of visual appearance.
To insert at a specific index, use `board.insertChild(index, shape)`, bearing in mind the reversed order for dir="column"
or dir="row".
- Add to a board with `board.addFlexLayout(): FlexLayout`; instance then accessible via `board.flex`.
IMPORTANT: When adding a flex layout to a container that already has children,
use `penpotUtils.addFlexLayout(container, dir)` instead! This preserves the existing visual order of children.
Otherwise, children will be arbitrarily reordered when the children order suddenly determines the display order.
- Check with: `if (board.flex) { ... }`
* **Flex Layout**: A flexbox-style layout system * **Grid Layout**: A CSS grid-style layout system
- Properties: `dir`, `rowGap`, `columnGap`, `alignItems`, `justifyContent`; - Add to a board with `board.addGridLayout(): GridLayout`; instance then accessibly via `board.grid`;
- `dir`: "row" | "column" | "row-reverse" | "column-reverse" Check with: `if (board.grid) { ... }`
- Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding` - Properties: `rows`, `columns`, `rowGap`, `columnGap`
- To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions. - Children are positioned via 1-based row/column indices
Optionally, adjust indivudual child margins via `child.layoutChild`. - Add to grid via `board.flex.appendChild(shape, row, column)`
- When a board has flex layout, - Modify grid positioning after the fact via `shape.layoutCell: LayoutCellProperties`
- child positions are controlled by the layout system, not by individual x/y coordinates (unless `child.layoutChild.absolute` is true);
appending or inserting children automatically positions them according to the layout rules.
- CRITICAL: For for dir="column" or dir="row", the order of the `children` array is reversed relative to the visual order!
Therefore, the element that appears first in the array, appears visually at the end (bottom/right) and vice versa.
ALWAYS BEAR IN MIND THAT THE CHILDREN ARRAY ORDER IS REVERSED FOR dir="column" OR dir="row"!
- CRITICAL: The FlexLayout method `board.flex.appendChild` is BROKEN. To append children to a flex layout board such that
they appear visually at the end, ALWAYS use the Board's method `board.appendChild(shape)`; it will insert at the front
of the `children` array for dir="column" or dir="row", which is what you want. So call it in the order of visual appearance.
To insert at a specific index, use `board.insertChild(index, shape)`, bearing in mind the reversed order for dir="column"
or dir="row".
- Add to a board with `board.addFlexLayout(): FlexLayout`; instance then accessible via `board.flex`.
IMPORTANT: When adding a flex layout to a container that already has children,
use `penpotUtils.addFlexLayout(container, dir)` instead! This preserves the existing visual order of children.
Otherwise, children will be arbitrarily reordered when the children order suddenly determines the display order.
- Check with: `if (board.flex) { ... }`
* **Grid Layout**: A CSS grid-style layout system * When working with boards:
- Add to a board with `board.addGridLayout(): GridLayout`; instance then accessibly via `board.grid`; - ALWAYS check if the board has a layout system before attempting to reposition children
Check with: `if (board.grid) { ... }` - Modify layout properties (gaps, padding) instead of trying to set child x/y positions directly
- Properties: `rows`, `columns`, `rowGap`, `columnGap` - Layout systems override manual positioning of children
- Children are positioned via 1-based row/column indices
- Add to grid via `board.flex.appendChild(shape, row, column)`
- Modify grid positioning after the fact via `shape.layoutCell: LayoutCellProperties`
* When working with boards: # Text Elements
- ALWAYS check if the board has a layout system before attempting to reposition children
- Modify layout properties (gaps, padding) instead of trying to set child x/y positions directly
- Layout systems override manual positioning of children
# Text Elements The rendered content of `Text` element is given by the `characters` property.
The rendered content of `Text` element is given by the `characters` property. To change the size of the text, change the `fontSize` property; applying `resize()` does NOT change the font size,
it only changes the formal bounding box; if the text does not fit it, it will overflow.
The bounding box is sized automatically as long as the `growType` property is set to "auto-width" or "auto-height".
`resize` always sets `growType` to "fixed", so ALWAYS set it back to "auto-*" if you want automatic sizing - otherwise the bounding box will be meaningless, with the text overflowing!
The auto-sizing is not immediate; sleep for a short time (100ms) if you want to read the updated bounding box.
To change the size of the text, change the `fontSize` property; applying `resize()` does NOT change the font size, # The `penpot` and `penpotUtils` Objects, Exploring Designs
it only changes the formal bounding box; if the text does not fit it, it will overflow.
The bounding box is sized automatically as long as the `growType` property is set to "auto-width" or "auto-height".
`resize` always sets `growType` to "fixed", so ALWAYS set it back to "auto-*" if you want automatic sizing - otherwise the bounding box will be meaningless, with the text overflowing!
The auto-sizing is not immediate; sleep for a short time (100ms) if you want to read the updated bounding box.
# The `penpot` and `penpotUtils` Objects, Exploring Designs A key object to use in your code is the `penpot` object (which is of type `Penpot`):
* `penpot.selection` provides the list of shapes the user has selected in the Penpot UI.
If it is unclear which elements to work on, you can ask the user to select them for you.
ALWAYS immediately copy the selected shape(s) into `storage`! Do not assume that the selection remains unchanged.
* `penpot.root` provides the root shape of the currently active page.
* Generation of CSS content for elements via `penpot.generateStyle`
* Generation of HTML/SVG content for elements via `penpot.generateMarkup`
A key object to use in your code is the `penpot` object (which is of type `Penpot`): For example, to generate CSS for the currently selected elements, you can execute this:
* `penpot.selection` provides the list of shapes the user has selected in the Penpot UI. return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true });
If it is unclear which elements to work on, you can ask the user to select them for you.
ALWAYS immediately copy the selected shape(s) into `storage`! Do not assume that the selection remains unchanged.
* `penpot.root` provides the root shape of the currently active page.
* Generation of CSS content for elements via `penpot.generateStyle`
* Generation of HTML/SVG content for elements via `penpot.generateMarkup`
For example, to generate CSS for the currently selected elements, you can execute this: CRITICAL: The `penpotUtils` object provides essential utilities - USE THESE INSTEAD OF WRITING YOUR OWN:
return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true }); * getPages(): { id: string; name: string }[]
* getPageById(id: string): Page | null
* getPageByName(name: string): Page | null
* shapeStructure(shape: Shape, maxDepth: number | undefined = undefined): { id, name, type, children?, layout? }
Generates an overview structure of the given shape.
- children: recursive, limited by maxDepth
- layout: present if shape has flex/grid layout, contains { type: "flex" | "grid", ... }
* findShapeById(id: string): Shape | null
* findShape(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape | null
If no root is provided, search globally (in all pages).
* findShapes(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape[]
* isContainedIn(shape: Shape, container: Shape): boolean
Returns true iff shape is fully within the container's geometric bounds.
Note that a shape's bounds may not always reflect its actual visual content - descendants can overflow; check using analyzeDescendants (see below).
* setParentXY(shape: Shape, parentX: number, parentY: number): void
Sets shape position relative to its parent (since parentX/parentY are read-only)
* analyzeDescendants<T>(root: Shape, evaluator: (root: Shape, descendant: Shape) => T | null | undefined, maxDepth?: number): Array<{ shape: Shape, result: T }>
General-purpose utility for analyzing/validating descendants
Calls evaluator on each descendant; collects non-null/undefined results
Powerful pattern: evaluator can return corrector functions or diagnostic data
* Further functions for specific tasks (described in the sections below)
CRITICAL: The `penpotUtils` object provides essential utilities - USE THESE INSTEAD OF WRITING YOUR OWN: General pointers for working with Penpot designs:
* getPages(): { id: string; name: string }[] * Prefer `penpotUtils` helper functions — avoid reimplementing shape searching.
* getPageById(id: string): Page | null * To get an overview of a single page, use `penpotUtils.shapeStructure(page.root, 3)`.
* getPageByName(name: string): Page | null Note that `penpot.root` refers to the current page only. When working across pages, first determine the relevant page(s).
* shapeStructure(shape: Shape, maxDepth: number | undefined = undefined): { id, name, type, children?, layout? } * Use `penpotUtils.findShapes()` or `penpotUtils.findShape()` with predicates to locate elements efficiently.
Generates an overview structure of the given shape.
- children: recursive, limited by maxDepth
- layout: present if shape has flex/grid layout, contains { type: "flex" | "grid", ... }
* findShapeById(id: string): Shape | null
* findShape(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape | null
If no root is provided, search globally (in all pages).
* findShapes(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape[]
* isContainedIn(shape: Shape, container: Shape): boolean
Returns true iff shape is fully within the container's geometric bounds.
Note that a shape's bounds may not always reflect its actual visual content - descendants can overflow; check using analyzeDescendants (see below).
* setParentXY(shape: Shape, parentX: number, parentY: number): void
Sets shape position relative to its parent (since parentX/parentY are read-only)
* analyzeDescendants<T>(root: Shape, evaluator: (root: Shape, descendant: Shape) => T | null | undefined, maxDepth?: number): Array<{ shape: Shape, result: T }>
General-purpose utility for analyzing/validating descendants
Calls evaluator on each descendant; collects non-null/undefined results
Powerful pattern: evaluator can return corrector functions or diagnostic data
* Further functions for specific tasks (described in the sections below)
General pointers for working with Penpot designs: Common tasks - Quick Reference (ALWAYS use penpotUtils for these):
* Prefer `penpotUtils` helper functions — avoid reimplementing shape searching. * Find all images:
* To get an overview of a single page, use `penpotUtils.shapeStructure(page.root, 3)`. const images = penpotUtils.findShapes(
Note that `penpot.root` refers to the current page only. When working across pages, first determine the relevant page(s). shape => shape.type === 'image' || shape.fills?.some(fill => fill.fillImage),
* Use `penpotUtils.findShapes()` or `penpotUtils.findShape()` with predicates to locate elements efficiently. penpot.root
);
* Find text elements:
const texts = penpotUtils.findShapes(shape => shape.type === 'text', penpot.root);
* Find (the first) shape with a given name:
const shape = penpotUtils.findShape(shape => shape.name === 'MyShape');
* Get structure of current selection:
const structure = penpotUtils.shapeStructure(penpot.selection[0]);
* Find shapes in current selection/board:
const shapes = penpotUtils.findShapes(predicate, penpot.selection[0] || penpot.root);
* Validate/analyze descendants (returning corrector functions):
const fixes = penpotUtils.analyzeDescendants(board, (root, shape) => {
const xMod = shape.parentX % 4;
if (xMod !== 0) {
return () => penpotUtils.setParentXY(shape, Math.round(shape.parentX / 4) * 4, shape.parentY);
}
});
fixes.forEach(f => f.result()); // Apply all fixes
* Find containment violations:
const violations = penpotUtils.analyzeDescendants(board, (root, shape) => {
return !penpotUtils.isContainedIn(shape, root) ? 'outside-bounds' : null;
});
Always validate against the root container that is supposed to contain the shapes.
Common tasks - Quick Reference (ALWAYS use penpotUtils for these): # Visual Inspection of Designs
* Find all images:
const images = penpotUtils.findShapes(
shape => shape.type === 'image' || shape.fills?.some(fill => fill.fillImage),
penpot.root
);
* Find text elements:
const texts = penpotUtils.findShapes(shape => shape.type === 'text', penpot.root);
* Find (the first) shape with a given name:
const shape = penpotUtils.findShape(shape => shape.name === 'MyShape');
* Get structure of current selection:
const structure = penpotUtils.shapeStructure(penpot.selection[0]);
* Find shapes in current selection/board:
const shapes = penpotUtils.findShapes(predicate, penpot.selection[0] || penpot.root);
* Validate/analyze descendants (returning corrector functions):
const fixes = penpotUtils.analyzeDescendants(board, (root, shape) => {
const xMod = shape.parentX % 4;
if (xMod !== 0) {
return () => penpotUtils.setParentXY(shape, Math.round(shape.parentX / 4) * 4, shape.parentY);
}
});
fixes.forEach(f => f.result()); // Apply all fixes
* Find containment violations:
const violations = penpotUtils.analyzeDescendants(board, (root, shape) => {
return !penpotUtils.isContainedIn(shape, root) ? 'outside-bounds' : null;
});
Always validate against the root container that is supposed to contain the shapes.
# Visual Inspection of Designs For many tasks, it can be critical to visually inspect the design. Remember to use the `export_shape` tool for this purpose!
For many tasks, it can be critical to visually inspect the design. Remember to use the `export_shape` tool for this purpose! # Revising Designs
# Revising Designs * Before applying design changes, ask: "Would a designer consider this appropriate?"
* When dealing with containment issues, ask: Is the parent too small OR is the child too large?
Container sizes are usually intentional, check content first.
* Check for reasonable font sizes and typefaces
* The use of flex layouts is encouraged for cases where elements are arranged in rows or columns with consistent spacing/positioning.
Consider converting boards to flex layout when appropriate.
* Before applying design changes, ask: "Would a designer consider this appropriate?" # Asset Libraries
* When dealing with containment issues, ask: Is the parent too small OR is the child too large?
Container sizes are usually intentional, check content first.
* Check for reasonable font sizes and typefaces
* The use of flex layouts is encouraged for cases where elements are arranged in rows or columns with consistent spacing/positioning.
Consider converting boards to flex layout when appropriate.
# Asset Libraries Libraries in Penpot are collections of reusable design assets (components, colors, and typographies) that can be shared across files.
They enable design systems and consistent styling across projects.
Each Penpot file has its own local library and can connect to external shared libraries.
Libraries in Penpot are collections of reusable design assets (components, colors, and typographies) that can be shared across files. Accessing libraries: via `penpot.library` (type: `LibraryContext`):
They enable design systems and consistent styling across projects. * `penpot.library.local` (type: `Library`) - The current file's own library
Each Penpot file has its own local library and can connect to external shared libraries. * `penpot.library.connected` (type: `Library[]`) - Array of already-connected external libraries
* `penpot.library.availableLibraries()` (returns: `Promise<LibrarySummary[]>`) - Libraries available to connect
* `penpot.library.connectLibrary(libraryId: string)` (returns: `Promise<Library>`) - Connect a new library
Accessing libraries: via `penpot.library` (type: `LibraryContext`): Each `Library` object has:
* `penpot.library.local` (type: `Library`) - The current file's own library * `id: string`
* `penpot.library.connected` (type: `Library[]`) - Array of already-connected external libraries * `name: string`
* `penpot.library.availableLibraries()` (returns: `Promise<LibrarySummary[]>`) - Libraries available to connect * `components: LibraryComponent[]` - Array of components
* `penpot.library.connectLibrary(libraryId: string)` (returns: `Promise<Library>`) - Connect a new library * `colors: LibraryColor[]` - Array of colors
* `typographies: LibraryTypography[]` - Array of typographies
Each `Library` object has: Using library components:
* `id: string` * find a component in the library by name:
* `name: string` const component: LibraryComponent = library.components.find(comp => comp.name.includes('Button'));
* `components: LibraryComponent[]` - Array of components * create a new instance of the component on the current page:
* `colors: LibraryColor[]` - Array of colors const instance: Shape = component.instance();
* `typographies: LibraryTypography[]` - Array of typographies This returns a `Shape` (often a `Board` containing child elements).
After instantiation, modify the instance's properties as desired.
* get the reference to the main component shape:
const mainShape: Shape = component.mainInstance();
Using library components: Adding assets to a library:
* find a component in the library by name: * const newColor: LibraryColor = penpot.library.local.createColor();
const component: LibraryComponent = library.components.find(comp => comp.name.includes('Button')); newColor.name = 'Brand Primary';
* create a new instance of the component on the current page: newColor.color = '#0066FF';
const instance: Shape = component.instance(); * const newTypo: LibraryTypography = penpot.library.local.createTypography();
This returns a `Shape` (often a `Board` containing child elements). newTypo.name = 'Heading Large';
After instantiation, modify the instance's properties as desired. // Set typography properties...
* get the reference to the main component shape: * const shapes: Shape[] = [shape1, shape2]; // shapes to include
const mainShape: Shape = component.mainInstance(); const newComponent: LibraryComponent = penpot.library.local.createComponent(shapes);
newComponent.name = 'My Button';
Adding assets to a library: # Design Tokens
* const newColor: LibraryColor = penpot.library.local.createColor();
newColor.name = 'Brand Primary';
newColor.color = '#0066FF';
* const newTypo: LibraryTypography = penpot.library.local.createTypography();
newTypo.name = 'Heading Large';
// Set typography properties...
* const shapes: Shape[] = [shape1, shape2]; // shapes to include
const newComponent: LibraryComponent = penpot.library.local.createComponent(shapes);
newComponent.name = 'My Button';
# Design Tokens Design tokens are reusable design values (colors, dimensions, typography, etc.) for consistent styling.
Design tokens are reusable design values (colors, dimensions, typography, etc.) for consistent styling. The token library: `penpot.library.local.tokens` (type: `TokenCatalog`)
* `sets: TokenSet[]` - Token collections (order matters for precedence)
* `themes: TokenTheme[]` - Presets that activate specific sets
* `addSet(name: string): TokenSet` - Create new set
* `addTheme(group: string, name: string): TokenTheme` - Create new theme
The token library: `penpot.library.local.tokens` (type: `TokenCatalog`) `TokenSet` contains tokens with unique names:
* `sets: TokenSet[]` - Token collections (order matters for precedence) * `active: boolean` - Only active sets affect shapes; use `set.toggleActive()` to change: `if (!set.active) set.toggleActive();`
* `themes: TokenTheme[]` - Presets that activate specific sets * `tokens: Token[]` - All tokens in set
* `addSet(name: string): TokenSet` - Create new set * `addToken(type: TokenType, name: string, value: TokenValueString): Token` - Creates a token, adding it to the set.
* `addTheme(group: string, name: string): TokenTheme` - Create new theme - `TokenType`: "color" | "dimension" | "spacing" | "typography" | "shadow" | "opacity" | "borderRadius" | "borderWidth" | "fontWeights" | "fontSizes" | "fontFamilies" | "letterSpacing" | "textDecoration" | "textCase"
- Examples:
const token = set.addToken("color", "color.primary", "#0066FF"); // direct value
const token2 = set.addToken("color", "color.accent", "{color.primary}"); // reference to another token
`TokenSet` contains tokens with unique names: `Token`:
* `active: boolean` - Only active sets affect shapes; use `set.toggleActive()` to change: `if (!set.active) set.toggleActive();` * `name: string` - Token name (may include group path like "color.base.white")
* `tokens: Token[]` - All tokens in set * `value: string | TokenValueString` - Raw value (may be direct value or reference to another token like "{color.primary}")
* `addToken(type: TokenType, name: string, value: TokenValueString): Token` - Creates a token, adding it to the set. * `resolvedValue` - Computed final value (follows references)
- `TokenType`: "color" | "dimension" | "spacing" | "typography" | "shadow" | "opacity" | "borderRadius" | "borderWidth" | "fontWeights" | "fontSizes" | "fontFamilies" | "letterSpacing" | "textDecoration" | "textCase" * `type: TokenType`
- Examples:
const token = set.addToken("color", "color.primary", "#0066FF"); // direct value
const token2 = set.addToken("color", "color.accent", "{color.primary}"); // reference to another token
`Token`: Discovering tokens:
* `name: string` - Token name (may include group path like "color.base.white") * `penpotUtils.tokenOverview()`: Maps from token set name to a mapping from token type to list of token names
* `value: string | TokenValueString` - Raw value (may be direct value or reference to another token like "{color.primary}") * `penpotUtils.findTokenByName(name: string): Token | null`: Finds the first applicable token matching the given name
* `resolvedValue` - Computed final value (follows references) * `penpotUtils.findTokensByName(name: string): Token[]`: Finds all tokens that match the given name across all token sets
* `type: TokenType` * `penpotUtils.getTokenSet(token: Token): TokenSet | null`: Gets the token set that contains the given token
Discovering tokens: Applying tokens:
* `penpotUtils.tokenOverview()`: Maps from token set name to a mapping from token type to list of token names * `shape.applyToken(token, properties: undefined | TokenProperty[])` - Apply a token to a shape for one or more properties
* `penpotUtils.findTokenByName(name: string): Token | null`: Finds the first applicable token matching the given name (if properties is undefined, use a default property based on the token type - not usually recommended).
* `penpotUtils.findTokensByName(name: string): Token[]`: Finds all tokens that match the given name across all token sets `TokenProperty` is a union type; here are some of the possible values:
* `penpotUtils.getTokenSet(token: Token): TokenSet | null`: Gets the token set that contains the given token - "all": applies the token to all properties it can control
- TokenBorderRadiusProps: "r1", "r2", "r3", "r4"
- TokenColorProps: "fill", "stroke"
- TokenDimensionProps: "x", "y", "stroke-width"
- TokenNumberProps: "rotation", "line-height"
- TokenSizingProps: "width", "height", "layout-item-min-w", "layout-item-max-w", "layout-item-min-h", "layout-item-max-h"
- TokenSpacingProps: "row-gap", "column-gap", "p1", "p2", "p3", "p4", "m1", "m2", "m3", "m4"
* `token.applyToShapes(shapes, properties)` - Apply from token
* Application is **asynchronous** (wait for ~100ms to see the effects)
* After application:
- `shape.tokens` returns a mapping `{ propertyName: "token.name" }`
- The properties that the tokens control will reflect the token's resolved value.
Applying tokens: Removing tokens:
* `shape.applyToken(token, properties: undefined | TokenProperty[])` - Apply a token to a shape for one or more properties Simply set the respective property directly - token binding is automatically removed, e.g.
(if properties is undefined, use a default property based on the token type - not usually recommended). shape.fills = [{ fillColor: "#000000", fillOpacity: 1 }]; // Removes fill token
`TokenProperty` is a union type; here are some of the possible values:
- "all": applies the token to all properties it can control
- TokenBorderRadiusProps: "r1", "r2", "r3", "r4"
- TokenColorProps: "fill", "stroke"
- TokenDimensionProps: "x", "y", "stroke-width"
- TokenNumberProps: "rotation", "line-height"
- TokenSizingProps: "width", "height", "layout-item-min-w", "layout-item-max-w", "layout-item-min-h", "layout-item-max-h"
- TokenSpacingProps: "row-gap", "column-gap", "p1", "p2", "p3", "p4", "m1", "m2", "m3", "m4"
* `token.applyToShapes(shapes, properties)` - Apply from token
* Application is **asynchronous** (wait for ~100ms to see the effects)
* After application:
- `shape.tokens` returns a mapping `{ propertyName: "token.name" }`
- The properties that the tokens control will reflect the token's resolved value.
Removing tokens: --
Simply set the respective property directly - token binding is automatically removed, e.g. You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.
shape.fills = [{ fillColor: "#000000", fillOpacity: 1 }]; // Removes fill token
--
You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.