Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Figma skill for writing directly to the Figma canvas through the MCP server and Plugin API.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
text-style-patterns.md
1# Text Style API Patterns23> Part of the [use_figma skill](../SKILL.md). How to create, apply, and inspect text styles using the Plugin API.4>5> For design system context (when to create text styles, how they relate to tokens, headless limitations), see [wwds-text-styles](working-with-design-systems/wwds-text-styles.md).67## Contents89- Listing Text Styles10- Creating a Text Style11- Probing Font Styles12- Creating a Type Ramp (Multi-Step)13- Importing Library Text Styles14- Applying Text Styles to Nodes1516## Listing Text Styles1718```javascript19/**20* Lists all local text styles with their key properties.21*22* @returns {Promise<Array<{id: string, name: string, key: string, fontSize: number, fontName: FontName, lineHeight: LineHeight, letterSpacing: LetterSpacing}>>}23*/24async function listTextStyles() {25const styles = await figma.getLocalTextStylesAsync();26return styles.map(s => ({27id: s.id,28name: s.name,29key: s.key,30fontSize: s.fontSize,31fontName: s.fontName,32lineHeight: s.lineHeight,33letterSpacing: s.letterSpacing34}));35}36```3738Full runnable script:3940```javascript41const results = await listTextStyles();42return results;43```4445## Creating a Text Style4647Font **MUST** be loaded before setting `fontName`. `lineHeight` and `letterSpacing` must be `{value, unit}` objects — bare numbers throw.4849```javascript50/**51* Creates a text style with all typographic properties set.52* Font MUST be loaded before calling.53*54* @param {string} name - Slash-delimited name, e.g. "body/base"55* @param {{ family: string, style: string }} fontName56* @param {number} fontSize - In pixels57* @param {{ value: number, unit: 'PIXELS' | 'PERCENT' } | { unit: 'AUTO' }} lineHeight58* @param {{ value: number, unit: 'PIXELS' | 'PERCENT' }} [letterSpacing]59* @param {string} [description] - e.g. the CSS variable name "CSS: var(--font-body-base)"60* @returns {TextStyle}61*/62function createTextStyleFull(name, fontName, fontSize, lineHeight, letterSpacing, description) {63const style = figma.createTextStyle();64style.name = name;65style.fontName = fontName;66style.fontSize = fontSize;67style.lineHeight = lineHeight; // { unit: 'AUTO' } | { value, unit: 'PIXELS'|'PERCENT' }68if (letterSpacing) style.letterSpacing = letterSpacing;69if (description) style.description = description;70return style;71}72```7374## Probing Font Styles7576Font style names vary per provider and per file (`"SemiBold"` vs `"Semi Bold"`). Always probe before hardcoding:7778```javascript79/**80* Probes available font styles for a given family.81* Useful when font style names are unknown (e.g. "SemiBold" vs "Semi Bold").82*83* @param {string} family - Font family name, e.g. "Inter"84* @param {string[]} stylesToTest - Candidate style names to probe85* @returns {Promise<string[]>} - Style names that loaded successfully86*/87async function probeAvailableFontStyles(family, stylesToTest) {88const available = [];89for (const style of stylesToTest) {90try {91await figma.loadFontAsync({ family, style });92available.push(style);93} catch (_) {}94}95return available;96}97```9899## Creating a Type Ramp (Multi-Step)100101Handles font loading, deduplication, and idempotency. Each entry: `[name, fontFamily, fontStyle, fontSize_px, lineHeight, cssVar]`.102103**HEADLESS NOTE:** `setBoundVariable` on `TextStyle` is not supported in `use_figma`. This function sets raw values. To bind variables, do it interactively in Figma after creation.104105```javascript106/**107* Creates a full type ramp from a token definition array.108* Handles font loading, deduplication, and idempotency.109*110* Each entry: [name, fontFamily, fontStyle, fontSize_px, lineHeight, cssVar]111* - lineHeight: { unit: 'AUTO' } or { value: number, unit: 'PIXELS' | 'PERCENT' }112*113* @param {Array} defs - Array of [name, fontFamily, fontStyle, fontSize, lineHeight, cssVar] tuples114* @returns {Promise<{ created: string[], skipped: string[] }>}115*/116async function createTypeRamp(defs) {117const uniqueFonts = new Set();118for (const [, family, style] of defs) {119uniqueFonts.add(JSON.stringify({ family, style }));120}121await Promise.all(122[...uniqueFonts].map(f => figma.loadFontAsync(JSON.parse(f)))123);124125const existing = new Set(126(await figma.getLocalTextStylesAsync()).map(s => s.name)127);128129const created = [];130const skipped = [];131132for (const [name, family, style, fontSize, lineHeight, cssVar] of defs) {133if (existing.has(name)) {134skipped.push(name);135continue;136}137const ts = figma.createTextStyle();138ts.name = name;139ts.fontName = { family, style };140ts.fontSize = fontSize;141ts.lineHeight = lineHeight ?? { unit: 'AUTO' };142if (cssVar) ts.description = `CSS: var(${cssVar})`;143created.push(name);144}145146return { created, skipped };147}148```149150Full runnable script:151152```javascript153const defs = [154['heading/xl', 'Inter', 'Bold', 48, { unit: 'PIXELS', value: 56 }, '--font-heading-xl'],155['heading/lg', 'Inter', 'Bold', 36, { unit: 'PIXELS', value: 44 }, '--font-heading-lg'],156['body/base', 'Inter', 'Regular', 16, { unit: 'AUTO' }, '--font-body-base'],157['body/sm', 'Inter', 'Regular', 14, { unit: 'AUTO' }, '--font-body-sm'],158['code/base', 'Roboto Mono', 'Regular', 14, { unit: 'AUTO' }, '--font-code-base'],159];160const result = await createTypeRamp(defs);161return result;162```163164## Importing Library Text Styles165166For text styles from **team libraries**, use `importStyleByKeyAsync`:167168```javascript169// Import a library text style by key170const headingStyle = await figma.importStyleByKeyAsync("TEXT_STYLE_KEY");171// Apply to a text node172await textNode.setTextStyleIdAsync(headingStyle.id);173```174175`search_design_system` with `includeStyles: true` returns style keys you can import this way. Prefer importing library styles over creating new ones.176177## Applying Text Styles to Nodes178179```javascript180/**181* Applies a text style to all TEXT nodes on the current page that match a given name pattern.182*183* @param {string} styleId - The ID of a TextStyle.184* @param {string} nodeNamePattern - Substring match against node names.185* @returns {Promise<number>} - Number of nodes the style was applied to.186*/187async function applyTextStyleToMatchingNodes(styleId, nodeNamePattern) {188const textNodes = figma.currentPage.findAllWithCriteria({ types: ['TEXT'] });189let applied = 0;190for (const node of textNodes) {191if (node.name.includes(nodeNamePattern)) {192await node.setTextStyleIdAsync(styleId);193applied++;194}195}196return applied;197}198```199200Full runnable script:201202```javascript203const applied = await applyTextStyleToMatchingNodes('STYLE_ID', 'Heading');204return { applied };205```206