mirror of
https://github.com/penpot/penpot.git
synced 2026-05-30 04:08:08 +00:00
509 lines
26 KiB
YAML
509 lines
26 KiB
YAML
# Prompts configuration for Penpot MCP Server
|
|
# This file contains various prompts and instructions that can be used by the server
|
|
|
|
initial_instructions: |
|
|
You have access to Penpot tools in order to interact with a Penpot design project directly.
|
|
As a precondition, the user must connect the Penpot design project to the MCP server using the Penpot MCP Plugin.
|
|
|
|
IMPORTANT: When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design.
|
|
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.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
# 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.
|
|
|
|
# Core Shape Properties and Methods
|
|
|
|
**Type**:
|
|
Any given shape contains information on the concrete type via its `type` field.
|
|
|
|
**Position and Dimensions**:
|
|
* 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.
|
|
|
|
**Other Writable Properties**:
|
|
* `name` - Shape name
|
|
* `fills`, `strokes` - Styling properties
|
|
* `rotation`, `opacity`, `blocked`, `hidden`, `visible`
|
|
|
|
**Z-Order**:
|
|
* The z-order of shapes is determined by the order in the `children` array of the parent shape.
|
|
Therefore, when creating shapes that should be on top of each other, add them to the parent in the correct order
|
|
(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).
|
|
|
|
**Modification Methods**:
|
|
* `resize(width, height)` - Change dimensions (required for width/height since they're read-only)
|
|
* `rotate(angle, center?)` - Rotate shape
|
|
* `remove()` - Permanently destroy the shape (use only for deletion, NOT for reparenting)
|
|
|
|
**Hierarchical Structure**:
|
|
* `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)
|
|
|
|
# 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.
|
|
|
|
# Layout Systems
|
|
|
|
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)
|
|
|
|
* **Flex Layout**: A flexbox-style layout system
|
|
- Properties: `dir`, `rowGap`, `columnGap`, `alignItems`, `justifyContent`;
|
|
- `dir`: "row" | "column" | "row-reverse" | "column-reverse"
|
|
- Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding`
|
|
- To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions.
|
|
Optionally, adjust indivudual child margins via `child.layoutChild`.
|
|
- 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) { ... }`
|
|
|
|
* **Grid Layout**: A CSS grid-style layout system
|
|
- Add to a board with `board.addGridLayout(): GridLayout`; instance then accessibly via `board.grid`;
|
|
Check with: `if (board.grid) { ... }`
|
|
- Properties: `rows`, `columns`, `rowGap`, `columnGap`
|
|
- 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:
|
|
- 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.
|
|
|
|
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.
|
|
|
|
# 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`
|
|
|
|
For example, to generate CSS for the currently selected elements, you can execute this:
|
|
return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true });
|
|
|
|
CRITICAL: The `penpotUtils` object provides essential utilities - USE THESE INSTEAD OF WRITING YOUR OWN:
|
|
* 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
|
|
|
|
General pointers for working with Penpot designs:
|
|
* Prefer `penpotUtils` helper functions — avoid reimplementing shape searching.
|
|
* To get an overview of a single page, use `penpotUtils.shapeStructure(page.root, 3)`.
|
|
Note that `penpot.root` refers to the current page only. When working across pages, first determine the relevant page(s).
|
|
* Use `penpotUtils.findShapes()` or `penpotUtils.findShape()` with predicates to locate elements efficiently.
|
|
|
|
Common tasks - Quick Reference (ALWAYS use penpotUtils for these):
|
|
* 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!
|
|
|
|
# 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.
|
|
|
|
# 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.
|
|
|
|
Accessing libraries: via `penpot.library` (type: `LibraryContext`):
|
|
* `penpot.library.local` (type: `Library`) - The current file's own library
|
|
* `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
|
|
|
|
Each `Library` object has:
|
|
* `id: string`
|
|
* `name: string`
|
|
* `components: LibraryComponent[]` - Array of components
|
|
* `colors: LibraryColor[]` - Array of colors
|
|
* `typographies: LibraryTypography[]` - Array of typographies
|
|
|
|
Using library components:
|
|
* find a component in the library by name:
|
|
const component: LibraryComponent = library.components.find(comp => comp.name.includes('Button'));
|
|
* create a new instance of the component on the current page:
|
|
const instance: Shape = component.instance();
|
|
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();
|
|
|
|
Adding assets to a library:
|
|
* 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 in Penpot are reusable design values (colors, dimensions, typography, shadows, etc.) that enable consistent styling across designs.
|
|
Tokens are organized in sets and can be controlled by themes.
|
|
|
|
## Accessing Tokens
|
|
|
|
Tokens are accessed through the library's token catalog:
|
|
* `penpot.library.local.tokens` (type: `TokenCatalog`) - The token catalog for the current file
|
|
|
|
The `TokenCatalog` contains:
|
|
* `sets: TokenSet[]` - Array of token sets (order matters: later sets override earlier ones for same-named tokens)
|
|
* `themes: TokenTheme[]` - Array of themes that control which sets are active
|
|
* `addSet(name: string): TokenSet` - Create a new token set
|
|
* `addTheme(group: string, name: string): TokenTheme` - Create a new theme
|
|
* `getSetById(id: string): TokenSet | undefined` - Retrieve a set by ID
|
|
* `getThemeById(id: string): TokenTheme | undefined` - Retrieve a theme by ID
|
|
|
|
## Token Sets
|
|
|
|
A `TokenSet` is a collection of tokens with unique names:
|
|
* `id: string` - Unique identifier
|
|
* `name: string` - Display name (may include group path separated by `/`)
|
|
* `active: boolean` - Whether this set is currently active (only active sets affect shapes)
|
|
* `tokens: Token[]` - Array of all tokens in this set
|
|
* `tokensByType: [string, Token[]][]` - Tokens grouped by type
|
|
* `toggleActive(): void` - Toggle the set's active status
|
|
* `addToken(type: TokenType, name: string, value: TokenValueString): Token` - Create and add a new token
|
|
* `getTokenById(id: string): Token | undefined` - Retrieve a token by ID
|
|
* `duplicate(): TokenSet` - Create a copy of this set
|
|
* `remove(): void` - Delete this set
|
|
|
|
## Token Themes
|
|
|
|
A `TokenTheme` is a preset that activates specific token sets:
|
|
* `id: string` - Unique identifier
|
|
* `group: string` - Theme group (only one theme per group can be active at a time)
|
|
* `name: string` - Display name
|
|
* `active: boolean` - Whether this theme is currently active
|
|
* `activeSets: TokenSet[]` - Array of sets activated by this theme
|
|
* `toggleActive(): void` - Activate/deactivate this theme
|
|
* `addSet(tokenSet: TokenSet): void` - Add a set to this theme
|
|
* `removeSet(tokenSet: TokenSet): void` - Remove a set from this theme
|
|
|
|
Themes in different groups can be active simultaneously (e.g., one for color scheme, one for density).
|
|
When a theme is activated, it activates its sets. Manually toggling a set deactivates all themes.
|
|
|
|
## Token Types
|
|
|
|
Tokens have specific types that determine what properties they can be applied to:
|
|
* `TokenColor` - Colors (can apply to: `"fill"`, `"stroke"`)
|
|
* `TokenDimension` - Measurements (can apply to: `"x"`, `"y"`, `"stroke-width"`)
|
|
* `TokenSpacing` - Spacing values
|
|
* `TokenSizing` - Size values
|
|
* `TokenTypography` - Complete typography styles (font family, size, weight, line height, etc.)
|
|
* `TokenShadow` - Shadow effects
|
|
* `TokenBorderRadius` - Border radius values
|
|
* `TokenOpacity` - Opacity values
|
|
* `TokenRotation` - Rotation angles
|
|
* And more...
|
|
|
|
Each token has:
|
|
* `id: string` - Unique identifier
|
|
* `name: string` - Token name (may include group path separated by `.`)
|
|
* `type: TokenType` - The token type (e.g., `"color"`, `"dimension"`)
|
|
* `value: string | TokenValueString` - The raw value (may be a direct value or a reference to another token)
|
|
* `resolvedValue: <type-specific> | undefined` - The computed final value (follows references)
|
|
* `description: string` - Optional description
|
|
* `duplicate(): Token` - Create a copy
|
|
* `remove(): void` - Delete this token
|
|
|
|
## Token References
|
|
|
|
Tokens can reference other tokens using curly brace syntax in their `value`:
|
|
* Direct value: `"#FFFFFF"` or `"16px"`
|
|
* Reference: `"{color.base.white}"` - references another token by name
|
|
|
|
The `resolvedValue` property contains the final computed value after following all references.
|
|
|
|
## Discovering Tokens
|
|
|
|
To explore available tokens in a project:
|
|
|
|
```javascript
|
|
const tokenCatalog = penpot.library.local.tokens;
|
|
|
|
// List all token sets
|
|
console.log("Token sets:", tokenCatalog.sets.map(s => ({
|
|
name: s.name,
|
|
active: s.active,
|
|
tokenCount: s.tokens.length
|
|
})));
|
|
|
|
// Find tokens by type
|
|
for (const set of tokenCatalog.sets) {
|
|
const colorTokens = set.tokens.filter(t => t.type === 'color');
|
|
console.log(`Set "${set.name}" has ${colorTokens.length} color tokens`);
|
|
}
|
|
|
|
// Find a specific token by name
|
|
let targetToken = null;
|
|
for (const set of tokenCatalog.sets) {
|
|
const token = set.tokens.find(t => t.name === 'color.base.white');
|
|
if (token) {
|
|
targetToken = token;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check which themes are active
|
|
console.log("Active themes:", tokenCatalog.themes.filter(t => t.active).map(t => t.name));
|
|
```
|
|
|
|
## Applying Tokens to Shapes
|
|
|
|
There are multiple ways to apply tokens to shapes:
|
|
|
|
**From the shape:**
|
|
```javascript
|
|
// Apply a token to specific properties
|
|
shape.applyToken(colorToken, ["fill"]);
|
|
|
|
// Apply to default properties (recommended - more reliable)
|
|
shape.applyToken(colorToken);
|
|
```
|
|
|
|
**From the token:**
|
|
```javascript
|
|
// Apply to multiple shapes
|
|
token.applyToShapes([shape1, shape2, shape3], ["fill"]);
|
|
|
|
// Apply to currently selected shapes
|
|
token.applyToSelected();
|
|
```
|
|
|
|
**Important notes on applying tokens:**
|
|
* Token application is **asynchronous** - there is a delay (typically ~100-150ms) before changes take effect
|
|
* Token application is **by name, not by ID** - if multiple tokens share the same name in different sets,
|
|
the active set with highest precedence (last in the sets array) determines which value is applied
|
|
* If you specify properties explicitly (e.g., `["fill"]`), in some cases it may be more reliable to omit
|
|
the properties parameter and let the token use its default properties
|
|
* After applying a token, `shape.tokens` will contain the mapping of properties to token names
|
|
|
|
**Reading applied tokens:**
|
|
```javascript
|
|
// Check which tokens are applied to a shape
|
|
console.log("Applied tokens:", shape.tokens);
|
|
// Example output: { fill: "color.base.white", typography: "typography.button" }
|
|
|
|
// The actual values are in the shape's properties
|
|
console.log("Actual fill color:", shape.fills[0].fillColor);
|
|
```
|
|
|
|
## Removing Tokens from Shapes
|
|
|
|
To remove a token from a shape, simply set the property directly:
|
|
|
|
```javascript
|
|
// Remove a fill color token by setting fills directly
|
|
shape.fills = [{
|
|
fillColor: "#000000",
|
|
fillOpacity: 1
|
|
}];
|
|
// The token binding is automatically removed from shape.tokens
|
|
|
|
// Remove a typography token by setting font properties
|
|
shape.fontSize = 16;
|
|
// This breaks the typography token binding
|
|
```
|
|
|
|
**The token removal mechanism is elegant:** Penpot automatically removes token bindings when you directly set
|
|
the corresponding property. You don't need an explicit "removeToken" method.
|
|
|
|
## Creating New Tokens
|
|
|
|
To create tokens, first create or access a token set, then add tokens to it:
|
|
|
|
```javascript
|
|
const tokenCatalog = penpot.library.local.tokens;
|
|
|
|
// Create a new token set (or use an existing one)
|
|
const mySet = tokenCatalog.addSet("my-tokens");
|
|
|
|
// Make sure the set is active
|
|
if (!mySet.active) {
|
|
mySet.toggleActive();
|
|
}
|
|
|
|
// Add a color token
|
|
const primaryColor = mySet.addToken("color", "color.primary", "#0066FF");
|
|
|
|
// Add a dimension token
|
|
const baseSpacing = mySet.addToken("dimension", "spacing.base", "8px");
|
|
|
|
// Add a token that references another token
|
|
const accentColor = mySet.addToken("color", "color.accent", "{color.primary}");
|
|
|
|
// Token names can include group paths with dots
|
|
const buttonPrimary = mySet.addToken("color", "color.button.primary", "#0066FF");
|
|
```
|
|
|
|
**Token value formats:**
|
|
* Colors: `"#RRGGBB"` or `"#RRGGBBAA"` or references like `"{color.base.white}"`
|
|
* Dimensions: `"16px"` or `"1.5rem"` or references
|
|
* Numbers: `"1.5"` or `"100"` or references
|
|
* Typography: Complex object or string representation
|
|
* Shadows: Array of shadow objects
|
|
|
|
## Creating and Using Themes
|
|
|
|
Themes allow you to switch between different token configurations:
|
|
|
|
```javascript
|
|
const tokenCatalog = penpot.library.local.tokens;
|
|
|
|
// Create theme groups (e.g., for color schemes)
|
|
const lightTheme = tokenCatalog.addTheme("color-scheme", "Light");
|
|
const darkTheme = tokenCatalog.addTheme("color-scheme", "Dark");
|
|
|
|
// Add sets to themes
|
|
const lightColorsSet = tokenCatalog.sets.find(s => s.name === "colors-light");
|
|
const darkColorsSet = tokenCatalog.sets.find(s => s.name === "colors-dark");
|
|
|
|
lightTheme.addSet(lightColorsSet);
|
|
darkTheme.addSet(darkColorsSet);
|
|
|
|
// Activate a theme (only one theme per group can be active)
|
|
darkTheme.toggleActive();
|
|
|
|
// When you switch themes, all shapes using tokens will update automatically
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
* Use semantic token names (e.g., `color.text.primary`) rather than literal names (e.g., `color.black`)
|
|
* Organize tokens in sets by purpose (e.g., "base-colors", "semantic-colors", "spacing")
|
|
* Use token references to create hierarchies (base tokens → semantic tokens → component tokens)
|
|
* Keep token sets active when you want their tokens to be available
|
|
* When applying tokens programmatically, account for the async nature (use small delays if you need to read results)
|
|
* Prefer omitting the `properties` parameter when applying tokens, unless you have a specific need
|
|
|
|
--
|
|
You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.
|