Typography
Typography group token types: Typography, Font Size, Font Weight, Font Family, Font Style, Line Height, Letter Spacing, Paragraph, Text Case, Text Decoration — full reference with examples.
The Typography group covers everything text-related: fonts, size, leading, tracking, style, and decorations.
Typography (composite)
A composite type combining all typographic properties in a single token.
Figma: exports as a Text Style.
Value structure:
{
"typography": {
"heading-xl": {
"$value": {
"fontFamily": "Inter",
"fontSize": "40px",
"fontWeight": 700,
"lineHeight": "48px",
"letterSpacing": "-0.02em",
"paragraphSpacing": "16px",
"textCase": "none",
"textDecoration": "none"
},
"$type": "typography"
}
}
}
Object properties:
| Property | Type | Description |
|---|---|---|
fontFamily | string | Font name |
fontSize | string / number | Font size |
fontWeight | number / string | Weight |
lineHeight | string / number | Line height |
letterSpacing | string / number | Letter spacing |
paragraphSpacing | string / number | Paragraph spacing |
paragraphIndent | string / number | First line indent |
textCase | string | Text case |
textDecoration | string | Decoration (underline etc.) |
fontStyle | string | Style (italic, normal) |
All properties are optional. Specify only those you need.
Aliases in properties:
{
"typography": {
"body": {
"$value": {
"fontFamily": "{fontFamily.primary}",
"fontSize": "{fontSize.md}",
"fontWeight": "{fontWeight.regular}",
"lineHeight": "{lineHeight.normal}"
},
"$type": "typography"
}
}
}
Font Size
Font size. A simple numeric token.
Figma: exports as a Number Variable (FLOAT).
Default Figma Scope: FONT_SIZE.
{
"fontSize": {
"$type": "fontSize",
"xs": { "$value": "12px" },
"sm": { "$value": "14px" },
"md": { "$value": "16px" },
"lg": { "$value": "18px" },
"xl": { "$value": "20px" },
"2xl": { "$value": "24px" },
"3xl": { "$value": "30px" },
"4xl": { "$value": "36px" },
"5xl": { "$value": "48px" }
}
}
Accepted formats: "16px", "1rem", 16.
Line Height
Line height (leading).
Figma: exports as a Number Variable (FLOAT).
Default Figma Scope: LINE_HEIGHT.
{
"lineHeight": {
"$type": "lineHeight",
"tight": { "$value": "1.25" },
"normal": { "$value": "1.5" },
"relaxed": { "$value": "1.75" },
"loose": { "$value": "2" }
}
}
Accepted formats: number (multiplier), "24px" (absolute), "150%" (percentage of fontSize).
Font Weight
Font weight. Accepts both numeric and string values.
Figma: exports as a String Variable (STRING).
Default Figma Scope: FONT_STYLE.
{
"fontWeight": {
"$type": "fontWeight",
"thin": { "$value": 100 },
"light": { "$value": 300 },
"regular": { "$value": 400 },
"medium": { "$value": 500 },
"semibold": { "$value": 600 },
"bold": { "$value": 700 },
"extrabold": { "$value": 800 },
"black": { "$value": 900 }
}
}
Numeric-to-string mapping table
| CSS number | Canonical name | Accepted aliases (case / spaces / hyphens / underscores are normalized) |
|---|---|---|
| 100 | Thin | hairline, hair line, HairLine, thin |
| 150 | Extra Thin | extrathin, extra thin, extra-thin, ultrathin |
| 200 | Extra Light | extralight, extra light, ultralight, ultra light, ExtraLight |
| 250 | Ultra Light | ultralight, ultra light, Ultra-Light |
| 300 | Light | light |
| 350 | Book | book, text |
| 400 | Regular | regular, normal, book, text, roman, plain, standard |
| 450 | Text | text |
| 500 | Medium | medium |
| 550 | Demi | demi |
| 600 | Semi Bold | semibold, SemiBold, Semibold, Semi-Bold, semi_bold, demibold, Demi Bold |
| 650 | Semi Bold | semibold, semi-bold |
| 700 | Bold | bold, strong |
| 750 | Extra Bold | extrabold, ExtraBold, extra-bold, Extra Bold |
| 800 | Extra Bold | extrabold, ultrabold, ultra bold, heavy, Heavy |
| 850 | Heavy | heavy |
| 900 | Black | black, heavy |
| 950 | Extra Black | extrablack, ExtraBlack, extra-black, ultrablack, Ultra Black |
How name resolution works: the plugin normalizes every input (lower-case, drops whitespace/hyphens/underscores) and looks it up in the alias map. So
"semiBold","Semi-Bold","SEMI_BOLD"and"Semibold"all resolve to the same weight.💡 When applied to a text layer the plugin reads the family from the layer (or from the
fontFamilytoken), queries the list of available font styles and picks an exact match on the normalized name. For example, SF Pro exposes the style as"Semibold", Inter as"Semi Bold", Roboto as"SemiBold"— all three satisfy a token value ofsemiBoldor600.💡 Export to Figma Variable: numeric values are converted to the canonical name (
700 → "Bold",600 → "Semi Bold",800 → "Extra Bold"). A keyword is preserved in its canonical form ("heavy" → "Heavy"), and any trailingitalicmodifier produces anItalicsuffix ("600 italic" → "Semi Bold Italic").
Font Family
Font family name.
Figma: exports as a String Variable (STRING).
Default Figma Scope: FONT_FAMILY.
Font stack (compound value): you can use a single string in the same form as in CSS — a comma-separated list of family names and generics. Example: "Satoshi, Arial, sans-serif". Figma accepts this as the font family value and applies it to text (the editor stores one string; fallback behavior follows Figma’s font resolution).
{
"fontFamily": {
"$type": "fontFamily",
"primary": { "$value": "Inter" },
"secondary": { "$value": "Playfair Display" },
"mono": { "$value": "JetBrains Mono" },
"sansStack": { "$value": "Satoshi, Arial, sans-serif" }
}
}
Important: for a single font name, it must match how the font is registered in Figma (exact spelling). For a stack, put your preferred installed fonts first; if none resolve, the text may not update as expected.
Font Style
fontStyle mirrors the Cmd/Ctrl + B / I / U shortcuts in Figma. A single token can carry any combination: weight, italic, underline, strikethrough, or a full reset.
Figma: not exported as a Variable (internal only). Applied directly to text layers.
Accepted values
The string may contain any combination of keywords separated by space, comma or +:
| Keyword | Effect |
|---|---|
bold, medium, semibold, light, thin, black, semi bold, extra bold, … | Sets the weight (fontName.style) |
italic | Enables italic |
no-italic / not-italic | Forcibly removes italic |
underline | textDecoration: UNDERLINE |
strikethrough / line-through | textDecoration: STRIKETHROUGH |
no-underline / no-strikethrough | Clears decoration |
none / regular / normal / reset / default (on its own) | Full reset: Regular, non-italic, no decoration |
{
"fontStyle": {
"$type": "fontStyle",
"normal": { "$value": "regular" },
"italic": { "$value": "italic" },
"bold": { "$value": "bold" },
"bold-italic": { "$value": "bold italic" },
"semibold": { "$value": "semi bold" },
"underline": { "$value": "underline" },
"bold-under": { "$value": "bold underline" },
"reset": { "$value": "none" }
}
}
Important limitation on text with a linked Text Style
The Figma Plugin API cannot change the face (Bold/Italic) of a text layer with a linked Text Style without breaking that link.
Native Cmd+B / Cmd+I produces a SEMANTIC_WEIGHT / SEMANTIC_ITALIC override internally, and this override is read-only to plugins — no public setter exists. Every plugin-accessible path for changing the face on a full text range (setRangeFontName, setRangeBoundVariable for fontStyle) detaches textStyleId.
Plugin behaviour in this scenario (text with a linked Text Style + fontStyle: bold/italic/… token):
- The plugin attempts to change the face.
- If the Text Style link is broken — the plugin rolls back via
setTextStyleIdAsync(originalStyleId). - The user sees a toast: "cannot apply Bold/Italic to styled text without detaching. Use Cmd+B/I manually."
- The token is not applied; the Text Style and its parameters (
fontSize,lineHeight,letterSpacing, etc.) are preserved.
For the intended character range, use Figma's native Cmd+B / Cmd+I / Cmd+U — they produce the SEMANTIC_* override correctly while keeping the Text Style intact.
What works without limitations
- Unlinked text.
fontStyle: bold/italic/bold italic/…applies normally viasetRangeFontName(there is no Text Style to break). - Decoration (
underline,strikethrough,no-underline,no-strikethrough) — applied viasetRangeTextDecorationand does not detach Text Style. Works on both linked and unlinked nodes. - Typography composite. When the typography token object contains a nested
fontStyle: "italic"field, the finalfontName.styleis assembled at style apply time — this is a different path from a stand-alonefontStyletoken and is unaffected by the limitation (the style is assigned as a whole rather than overridden on top). - Reset (
fontStyle: none/regular/normal) on a linked node — the plugin callssetTextStyleIdAsync(styleId)to revert the node to the style's defaults.
Preserving the axes not mentioned in the token
On unlinked nodes the merge is axis-aware: italic over Semi Bold → Semi Bold Italic (weight kept), bold over Regular Italic → Bold Italic (italic kept). This matches native Cmd+B / Cmd+I.
Interaction with exported Text Styles
- When a node has a linked Figma Text Style, the plugin will not modify the style or its parameters. A
fontStyletoken on top of it will be skipped (see limitation above) — use Cmd+B/I. - The typography composite (object token with a nested
fontStyle: "italic"field) assembles a freshfontName.stylewhen assigning the style and is not affected by the limitation.
Limitations (Figma, not the plugin)
- The Plugin API does not expose setters for the native
SEMANTIC_WEIGHT/SEMANTIC_ITALICoverrides — this is a general Figma restriction that no plugin can work around. setRangeFontNameandsetRangeBoundVariable("fontStyle", …)detachtextStyleIdwhen applied to the full range.setBoundVariable("fontStyle", …)at node level preserves the Text Style but is visually inert (the Text Style wins).- For families missing a required combination (e.g.
Roboto MonowithoutItalic) the plugin leaves the node unchanged and logs a warning in the main-thread console. - If you used earlier plugin builds that applied
fontStyleto text-styled nodes, your file may contain a hidden variable collection named_SXL Studio · fontStyle overrides. The plugin no longer creates or uses it; you can safely delete it from Figma's Variables UI.
Letter Spacing
Letter spacing (tracking).
Figma: exports as a Number Variable (FLOAT).
Default Figma Scope: LETTER_SPACING.
{
"letterSpacing": {
"$type": "letterSpacing",
"tighter": { "$value": "-0.05em" },
"tight": { "$value": "-0.025em" },
"normal": { "$value": "0em" },
"wide": { "$value": "0.025em" },
"wider": { "$value": "0.05em" },
"widest": { "$value": "0.1em" }
}
}
Accepted formats: "0.5px", "-0.02em", "2%", 0.
Paragraph Indent
First-line paragraph indent.
Figma: exports as a Number Variable (FLOAT).
Default Figma Scope: PARAGRAPH_INDENT.
{
"paragraphIndent": {
"$type": "paragraphIndent",
"none": { "$value": "0px" },
"sm": { "$value": "16px" },
"md": { "$value": "24px" }
}
}
Paragraph Spacing
Spacing between paragraphs.
Figma: exports as a Number Variable (FLOAT).
Default Figma Scope: PARAGRAPH_SPACING.
{
"paragraphSpacing": {
"$type": "paragraphSpacing",
"none": { "$value": "0px" },
"sm": { "$value": "8px" },
"md": { "$value": "16px" },
"lg": { "$value": "24px" }
}
}
Text Case
Text case transformation.
Figma: does not export as a Variable (internal only). Applied directly.
{
"textCase": {
"$type": "textCase",
"none": { "$value": "none" },
"uppercase": { "$value": "uppercase" },
"lowercase": { "$value": "lowercase" },
"capitalize": { "$value": "capitalize" }
}
}
CSS: text-transform.
Text Decoration
Text decoration: underline, strikethrough.
Figma: does not export as a Variable (internal only). Applied directly.
{
"textDecoration": {
"$type": "textDecoration",
"none": { "$value": "none" },
"underline": { "$value": "underline" },
"line-through": { "$value": "line-through" }
}
}
CSS: text-decoration.
Full example: typography system
{
"fontFamily": {
"$type": "fontFamily",
"sans": { "$value": "Inter" },
"serif": { "$value": "Merriweather" },
"mono": { "$value": "JetBrains Mono" }
},
"fontSize": {
"$type": "fontSize",
"xs": { "$value": "12px" },
"sm": { "$value": "14px" },
"base": { "$value": "16px" },
"lg": { "$value": "18px" },
"xl": { "$value": "20px" },
"2xl": { "$value": "24px" },
"3xl": { "$value": "30px" },
"4xl": { "$value": "36px" }
},
"fontWeight": {
"$type": "fontWeight",
"regular": { "$value": 400 },
"medium": { "$value": 500 },
"semibold": { "$value": 600 },
"bold": { "$value": 700 }
},
"lineHeight": {
"$type": "lineHeight",
"tight": { "$value": "1.25" },
"normal": { "$value": "1.5" },
"relaxed": { "$value": "1.75" }
},
"letterSpacing": {
"$type": "letterSpacing",
"tight": { "$value": "-0.025em" },
"normal": { "$value": "0em" },
"wide": { "$value": "0.025em" }
},
"typography": {
"display": {
"$value": {
"fontFamily": "{fontFamily.sans}",
"fontSize": "{fontSize.4xl}",
"fontWeight": "{fontWeight.bold}",
"lineHeight": "{lineHeight.tight}",
"letterSpacing": "{letterSpacing.tight}"
},
"$type": "typography"
},
"heading": {
"$value": {
"fontFamily": "{fontFamily.sans}",
"fontSize": "{fontSize.2xl}",
"fontWeight": "{fontWeight.semibold}",
"lineHeight": "{lineHeight.tight}"
},
"$type": "typography"
},
"body": {
"$value": {
"fontFamily": "{fontFamily.sans}",
"fontSize": "{fontSize.base}",
"fontWeight": "{fontWeight.regular}",
"lineHeight": "{lineHeight.normal}"
},
"$type": "typography"
},
"caption": {
"$value": {
"fontFamily": "{fontFamily.sans}",
"fontSize": "{fontSize.xs}",
"fontWeight": "{fontWeight.regular}",
"lineHeight": "{lineHeight.normal}",
"letterSpacing": "{letterSpacing.wide}"
},
"$type": "typography"
},
"code": {
"$value": {
"fontFamily": "{fontFamily.mono}",
"fontSize": "{fontSize.sm}",
"fontWeight": "{fontWeight.regular}",
"lineHeight": "{lineHeight.relaxed}"
},
"$type": "typography"
}
}
}
Related pages
- Overview: Tokens overview
- Styles: Color, Gradient, Image, Fill, Opacity
- Effects: Shadow, Blur, Glass, Effects