From 8d90edcc2fce951d9a1ec3be79a63461c1892c8f Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sat, 7 Feb 2026 20:42:03 +0100 Subject: [PATCH] :sparkles: Add instructions on design tokens --- mcp/packages/server/data/prompts.yml | 241 +++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) diff --git a/mcp/packages/server/data/prompts.yml b/mcp/packages/server/data/prompts.yml index dabed7a590..08646cd7c8 100644 --- a/mcp/packages/server/data/prompts.yml +++ b/mcp/packages/server/data/prompts.yml @@ -263,5 +263,246 @@ initial_instructions: | 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: | 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.