Text Style API Patterns
Part of the use_figma skill. How to create, apply, and inspect text styles using the Plugin API.
>
For design system context (when to create text styles, how they relate to tokens, headless limitations), see wwds-text-styles.
Contents
- Listing Text Styles
- Creating a Text Style
- Probing Font Styles
- Creating a Type Ramp (Multi-Step)
- Importing Library Text Styles
- Applying Text Styles to Nodes
Listing Text Styles
/**
* Lists all local text styles with their key properties.
*
* @returns {Promise<Array<{id: string, name: string, key: string, fontSize: number, fontName: FontName, lineHeight: LineHeight, letterSpacing: LetterSpacing}>>}
*/
async function listTextStyles() {
const styles = await figma.getLocalTextStylesAsync();
return styles.map(s => ({
id: s.id,
name: s.name,
key: s.key,
fontSize: s.fontSize,
fontName: s.fontName,
lineHeight: s.lineHeight,
letterSpacing: s.letterSpacing
}));
}Full runnable script:
const results = await listTextStyles();
return results;Creating a Text Style
Font MUST be loaded before setting fontName. lineHeight and letterSpacing must be {value, unit} objects — bare numbers throw.
/**
* Creates a text style with all typographic properties set.
* Font MUST be loaded before calling.
*
* @param {string} name - Slash-delimited name, e.g. "body/base"
* @param {{ family: string, style: string }} fontName
* @param {number} fontSize - In pixels
* @param {{ value: number, unit: 'PIXELS' | 'PERCENT' } | { unit: 'AUTO' }} lineHeight
* @param {{ value: number, unit: 'PIXELS' | 'PERCENT' }} [letterSpacing]
* @param {string} [description] - e.g. the CSS variable name "CSS: var(--font-body-base)"
* @returns {TextStyle}
*/
function createTextStyleFull(name, fontName, fontSize, lineHeight, letterSpacing, description) {
const style = figma.createTextStyle();
style.name = name;
style.fontName = fontName;
style.fontSize = fontSize;
style.lineHeight = lineHeight; // { unit: 'AUTO' } | { value, unit: 'PIXELS'|'PERCENT' }
if (letterSpacing) style.letterSpacing = letterSpacing;
if (description) style.description = description;
return style;
}Probing Font Styles
Font style names vary per provider and per file ("SemiBold" vs "Semi Bold"). Always probe before hardcoding:
/**
* Probes available font styles for a given family.
* Useful when font style names are unknown (e.g. "SemiBold" vs "Semi Bold").
*
* @param {string} family - Font family name, e.g. "Inter"
* @param {string[]} stylesToTest - Candidate style names to probe
* @returns {Promise<string[]>} - Style names that loaded successfully
*/
async function probeAvailableFontStyles(family, stylesToTest) {
const available = [];
for (const style of stylesToTest) {
try {
await figma.loadFontAsync({ family, style });
available.push(style);
} catch (_) {}
}
return available;
}Creating a Type Ramp (Multi-Step)
Handles font loading, deduplication, and idempotency. Each entry: [name, fontFamily, fontStyle, fontSize_px, lineHeight, cssVar].
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.
/**
* Creates a full type ramp from a token definition array.
* Handles font loading, deduplication, and idempotency.
*
* Each entry: [name, fontFamily, fontStyle, fontSize_px, lineHeight, cssVar]
* - lineHeight: { unit: 'AUTO' } or { value: number, unit: 'PIXELS' | 'PERCENT' }
*
* @param {Array} defs - Array of [name, fontFamily, fontStyle, fontSize, lineHeight, cssVar] tuples
* @returns {Promise<{ created: string[], skipped: string[] }>}
*/
async function createTypeRamp(defs) {
const uniqueFonts = new Set();
for (const [, family, style] of defs) {
uniqueFonts.add(JSON.stringify({ family, style }));
}
await Promise.all(
[...uniqueFonts].map(f => figma.loadFontAsync(JSON.parse(f)))
);
const existing = new Set(
(await figma.getLocalTextStylesAsync()).map(s => s.name)
);
const created = [];
const skipped = [];
for (const [name, family, style, fontSize, lineHeight, cssVar] of defs) {
if (existing.has(name)) {
skipped.push(name);
continue;
}
const ts = figma.createTextStyle();
ts.name = name;
ts.fontName = { family, style };
ts.fontSize = fontSize;
ts.lineHeight = lineHeight ?? { unit: 'AUTO' };
if (cssVar) ts.description = `CSS: var(${cssVar})`;
created.push(name);
}
return { created, skipped };
}Full runnable script:
const defs = [
['heading/xl', 'Inter', 'Bold', 48, { unit: 'PIXELS', value: 56 }, '--font-heading-xl'],
['heading/lg', 'Inter', 'Bold', 36, { unit: 'PIXELS', value: 44 }, '--font-heading-lg'],
['body/base', 'Inter', 'Regular', 16, { unit: 'AUTO' }, '--font-body-base'],
['body/sm', 'Inter', 'Regular', 14, { unit: 'AUTO' }, '--font-body-sm'],
['code/base', 'Roboto Mono', 'Regular', 14, { unit: 'AUTO' }, '--font-code-base'],
];
const result = await createTypeRamp(defs);
return result;Importing Library Text Styles
For text styles from team libraries, use importStyleByKeyAsync:
// Import a library text style by key
const headingStyle = await figma.importStyleByKeyAsync("TEXT_STYLE_KEY");
// Apply to a text node
await textNode.setTextStyleIdAsync(headingStyle.id);search_design_system with includeStyles: true returns style keys you can import this way. Prefer importing library styles over creating new ones.
Applying Text Styles to Nodes
/**
* Applies a text style to all TEXT nodes on the current page that match a given name pattern.
*
* @param {string} styleId - The ID of a TextStyle.
* @param {string} nodeNamePattern - Substring match against node names.
* @returns {Promise<number>} - Number of nodes the style was applied to.
*/
async function applyTextStyleToMatchingNodes(styleId, nodeNamePattern) {
const textNodes = figma.currentPage.findAllWithCriteria({ types: ['TEXT'] });
let applied = 0;
for (const node of textNodes) {
if (node.name.includes(nodeNamePattern)) {
await node.setTextStyleIdAsync(styleId);
applied++;
}
}
return applied;
}Full runnable script:
const applied = await applyTextStyleToMatchingNodes('STYLE_ID', 'Heading');
return { applied };