/**
* Extract text content and CSS typography properties from Figma text nodes.
*/
import {colorToCSS, type FigmaPaint} from './color.js';
export interface TextCSS {
content: string;
fontFamily?: string;
fontWeight?: number;
fontSize?: string;
lineHeight?: string;
textAlign?: string;
color?: string;
letterSpacing?: string;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface TextNode {
type?: string;
characters?: string;
style?: {
fontFamily?: string;
fontWeight?: number;
fontSize?: number;
lineHeightPx?: number;
textAlignHorizontal?: string;
letterSpacing?: number;
[key: string]: any;
};
fills?: FigmaPaint[];
[key: string]: any;
}
const TEXT_ALIGN_MAP: Record<string, string> = {
LEFT: 'left',
CENTER: 'center',
RIGHT: 'right',
JUSTIFIED: 'justify',
};
/**
* Extract typography CSS from a Figma TEXT node.
* Returns undefined for non-text nodes.
*/
export function extractText(node: TextNode): TextCSS | undefined {
if (node.type !== 'TEXT' || !node.characters) return undefined;
const css: TextCSS = {
content: node.characters,
};
const style = node.style;
if (style) {
if (style.fontFamily) css.fontFamily = style.fontFamily;
if (style.fontWeight) css.fontWeight = style.fontWeight;
if (style.fontSize) css.fontSize = `${style.fontSize}px`;
if (style.lineHeightPx && style.fontSize) {
const em = Number((style.lineHeightPx / style.fontSize).toFixed(2));
css.lineHeight = `${em}em`;
}
if (style.textAlignHorizontal && TEXT_ALIGN_MAP[style.textAlignHorizontal]) {
css.textAlign = TEXT_ALIGN_MAP[style.textAlignHorizontal];
}
if (style.letterSpacing && style.letterSpacing !== 0) {
css.letterSpacing = `${style.letterSpacing}px`;
}
}
// Extract color from first visible fill
if (node.fills?.length) {
for (const fill of node.fills) {
if (fill.visible === false) continue;
if (fill.type === 'SOLID' && fill.color) {
css.color = colorToCSS(fill.color, fill.opacity);
break;
}
}
}
return css;
}