Tokens

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:

JSON
{
  "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:

PropertyTypeDescription
fontFamilystringFont name
fontSizestring / numberFont size
fontWeightnumber / stringWeight
lineHeightstring / numberLine height
letterSpacingstring / numberLetter spacing
paragraphSpacingstring / numberParagraph spacing
paragraphIndentstring / numberFirst line indent
textCasestringText case
textDecorationstringDecoration (underline etc.)
fontStylestringStyle (italic, normal)

All properties are optional. Specify only those you need.

Aliases in properties:

JSON
{
  "typography": {
    "body": {
      "$value": {
        "fontFamily": "{fontFamily.primary}",
        "fontSize": "{fontSize.md}",
        "fontWeight": "{fontWeight.regular}",
        "lineHeight": "{lineHeight.normal}"
      },
      "$type": "typography"
    }
  }
}
Typography system: heading, body, caption

Font Size

Font size. A simple numeric token.

Figma: exports as a Number Variable (FLOAT).

Default Figma Scope: FONT_SIZE.

JSON
{
  "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.

JSON
{
  "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.

JSON
{
  "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 numberCanonical nameAccepted aliases (case / spaces / hyphens / underscores are normalized)
100Thinhairline, hair line, HairLine, thin
150Extra Thinextrathin, extra thin, extra-thin, ultrathin
200Extra Lightextralight, extra light, ultralight, ultra light, ExtraLight
250Ultra Lightultralight, ultra light, Ultra-Light
300Lightlight
350Bookbook, text
400Regularregular, normal, book, text, roman, plain, standard
450Texttext
500Mediummedium
550Demidemi
600Semi Boldsemibold, SemiBold, Semibold, Semi-Bold, semi_bold, demibold, Demi Bold
650Semi Boldsemibold, semi-bold
700Boldbold, strong
750Extra Boldextrabold, ExtraBold, extra-bold, Extra Bold
800Extra Boldextrabold, ultrabold, ultra bold, heavy, Heavy
850Heavyheavy
900Blackblack, heavy
950Extra Blackextrablack, 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 fontFamily token), 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 of semiBold or 600.

💡 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 trailing italic modifier produces an Italic suffix ("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).

JSON
{
  "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 +:

KeywordEffect
bold, medium, semibold, light, thin, black, semi bold, extra bold, …Sets the weight (fontName.style)
italicEnables italic
no-italic / not-italicForcibly removes italic
underlinetextDecoration: UNDERLINE
strikethrough / line-throughtextDecoration: STRIKETHROUGH
no-underline / no-strikethroughClears decoration
none / regular / normal / reset / default (on its own)Full reset: Regular, non-italic, no decoration
JSON
{
  "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):

  1. The plugin attempts to change the face.
  2. If the Text Style link is broken — the plugin rolls back via setTextStyleIdAsync(originalStyleId).
  3. The user sees a toast: "cannot apply Bold/Italic to styled text without detaching. Use Cmd+B/I manually."
  4. 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 via setRangeFontName (there is no Text Style to break).
  • Decoration (underline, strikethrough, no-underline, no-strikethrough) — applied via setRangeTextDecoration and 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 final fontName.style is assembled at style apply time — this is a different path from a stand-alone fontStyle token 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 calls setTextStyleIdAsync(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 BoldSemi Bold Italic (weight kept), bold over Regular ItalicBold 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 fontStyle token 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 fresh fontName.style when 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_ITALIC overrides — this is a general Figma restriction that no plugin can work around.
  • setRangeFontName and setRangeBoundVariable("fontStyle", …) detach textStyleId when 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 Mono without Italic) the plugin leaves the node unchanged and logs a warning in the main-thread console.
  • If you used earlier plugin builds that applied fontStyle to 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.

JSON
{
  "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.

JSON
{
  "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.

JSON
{
  "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.

JSON
{
  "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.

JSON
{
  "textDecoration": {
    "$type": "textDecoration",
    "none": { "$value": "none" },
    "underline": { "$value": "underline" },
    "line-through": { "$value": "line-through" }
  }
}

CSS: text-decoration.


Full example: typography system

JSON
{
  "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"
    }
  }
}