Updated October 22, 2026 11 min read
Middle Workflow

Figma Typography Variables: Text Styles as Tokens

Build - structure tokens & Variables

Figma has no composite typography variable. Bind font family, size, weight, and line height to Variables inside text Styles, then export them to code.

Figma has no typography variable. A typography token in Figma is a text Style whose individual properties - font family, size, weight, line height, letter spacing - are bound to Variables. The Style holds the composite; the Variables hold the values, the aliases, and the mode-specific overrides. That split is the whole model, and most of the confusion around typography tokens comes from expecting one object to do both jobs.

Atomize scans a file, builds Primitives → Atoms, and audits coverage across fills, strokes, spacing, and typography, so we spend a lot of time looking at where type sits outside the token system. The pattern is consistent: color and spacing get tokenized first, typography gets left as a list of text Styles with hardcoded sizes. This guide covers why that happens, how to bind Variables into text Styles without breaking the composite, and how typography tokens reach code.

The underlying architecture is covered in our Figma design tokens guide. This article is the typography-specific layer: the property mapping, the collection structure, and the export pitfalls that only appear once line heights and font families leave Figma.

In short

  • Figma Variables hold single values. No Variable type holds a full text Style.
  • Typography tokens are text Styles with their properties bound to Variables.
  • Font family, size, weight, line height, letter spacing, paragraph spacing, and paragraph indent can each be bound.
  • The composite stays intact: the Style still applies every property at once, and the Variables supply the values.
  • Modes make one text Style serve light, dark, or responsive scales without duplicating Styles.
  • Variable fonts are unrelated. They are a font file feature, not a Figma Variables feature.

Why Figma has no typography Variable

Every Figma Variable stores one value of one type - a color, a number, a string, a boolean. A text Style stores a set of values that apply together: family, size, weight, line height, letter spacing, and paragraph settings. There is no single value to assign to a typography Variable because the thing being stored is a bundle, not a point.

The table below separates three concepts that get mixed up in almost every typography discussion. Only the middle one is a token mechanism.

Text styles, typography variables, and variable fonts

Text StyleTypography variableVariable font
What it isA named composite of typography propertiesA Variable bound to one property inside a text StyleA single font file with weight or width axes
Where it livesFigma Styles panelFigma Variables panelThe font file itself
Holds several propertiesYesNo - one property per VariableNot applicable
Supports aliases and modesOnly through bound VariablesYesNo
Reaches code as a tokenAs a composite typography tokenAs a number or string valueAs a font asset

A variable font is the odd row. It lets one file interpolate between weights or widths, which is genuinely useful for responsive type, but it has nothing to do with Figma Variables. A team can use variable fonts with a fully tokenized type system, or use neither. Confusing the two leads to conversations where one person is talking about font files and the other about token bindings, and neither notices.

The practical consequence: there is no single “typography token” object to export from Figma Variables. The composite exists as a text Style, and the values exist as Variables. A complete typography token has to be assembled from both.

What breaks when typography stays outside the token system

Typography is usually the last layer to get tokenized, and the delay has a visible cost. It shows up in four places, none of which look like a typography problem until you trace them back.

  • Responsive scales become duplicated Styles. A mobile, tablet, and desktop ramp means three text Styles per role, maintained by hand. A mode on one Style replaces the copies.
  • Rebrands stall at fonts. Swapping a family means touching every Style that names it directly. A string Variable for family turns a rebuild into a value change.
  • Coverage audits look cleaner than the file is. If a scan counts fills and strokes, a file with fully hardcoded type can still report healthy coverage.
  • Code drifts by one pixel. Line heights and letter spacing are the properties designers tweak most often and engineers copy least reliably. A token file settles the argument.

The third point is the one we see most often. A file can have excellent color coverage and a typography layer that has never been bound, because the audit was scoped to the properties the team already cared about. Typography then becomes invisible debt: every screen looks right, and no report mentions it.

The fix is not to tokenize every property at once. It is to decide which typography decisions are system decisions - family, scale, weight ramp, line height rhythm - and bind those, while leaving one-off display treatments as plain Styles. The split below makes that decision concrete.

How to build typography variables in Figma

Build the value layer first, then bind it into text Styles. Doing it in the other order produces Styles that reference raw numbers and must be rebuilt once the token layer exists.

  1. Create a primitive type scale as Number Variables. Name steps by size, not by role: font/size/12, font/size/14, font/size/16, font/size/20, font/size/24, font/size/32. Add primitives for weight, line height, and letter spacing in the same collection.
  2. Add String Variables for font families. One string per family, such as font/family/base and font/family/display. A family is a value like any other, and a string Variable is what lets a rebrand change it in one place.
  3. Create a semantic typography collection. Each entry aliases a primitive and names a role: text/body/md, text/heading/lg, text/caption. The role names are what designers and engineers will actually discuss.
  4. Build the text Style. Set family, size, weight, line height, and letter spacing on the Style, then bind each property to the matching semantic Variable instead of typing a value.
  5. Repeat for every role in the ramp, then bind the Styles to text layers. The layer keeps the Style; the Style keeps the Variables; the Variables keep the modes.

The token mapping below shows the shape of the two collections. The primitive layer holds raw values and the semantic layer aliases them, exactly as it does for color:

/* Primitives - raw values, no aliases */
font/size/14      =  14px
font/size/16      =  16px
font/size/24      =  24px
font/weight/400   =  400
font/weight/600   =  600
lineheight/150%   =  150%
family/base       =  Inter

/* Semantic typography - aliases only */
text/body/md/family        →  family/base
text/body/md/size          →  font/size/16
text/body/md/weight        →  font/weight/400
text/body/md/line-height   →  lineheight/150%

text/heading/lg/size       →  font/size/24
text/heading/lg/weight     →  font/weight/600

The two-collection split is not ceremony. It keeps the semantic layer small enough to reason about while the primitive layer absorbs new steps without renaming roles. It also makes coverage measurable: a semantic entry that points at a raw value instead of a primitive is visible in the file, and a text Style with an unbound property is visible in the Styles panel.

Binding Variables into text Styles

The binding step is where the composite survives. A text Style keeps its identity as a Style, and each property inside it can point at a Variable. Figma’s Variables documentation lists the text Style properties that accept a binding: font family, size, weight, line height, letter spacing, paragraph spacing, and paragraph indent.

Two details matter in practice. Line height and letter spacing can be expressed in different units, and the unit travels with the value - a percentage line height behaves differently from a pixel line height when the font size changes. Decide the unit per role before binding, not after. The second detail is the font itself: a text Style still names a font, and binding the family does not load the font file. If the family is missing from the document, the Style falls back before any token logic runs.

Bind on the Style, not on individual text layers, unless the layer is a deliberate exception. Binding per layer multiplies the number of places a value must be updated and hides the typography system inside the canvas. The Style panel is the place where a team can see the whole ramp at once.

There is one trade-off worth naming. A Style with bound Variables is no longer portable to a file that does not have those Variables or a matching library. That is usually fine - portability without the token set is not portability worth having - but it does mean the library has to be published and consumed before the Styles travel.

Modes for responsive type and themes

Modes are what make typography variables worth the setup. A single text/heading/lg Style can hold desktop, tablet, and mobile values in separate modes, so a design switches its entire type scale by changing the mode rather than swapping Styles. The same mechanism handles light and dark themes when a family or weight changes between them.

Two rules keep typography modes manageable. First, every mode must define the same set of semantic entries - a missing value in tablet mode is a broken design, not a fallback. Second, keep the number of modes small. Three breakpoints is a system; twelve is a spreadsheet with a Figma interface.

Modes also expose a structural question: should line height scale with font size, or stay fixed per breakpoint? Both are valid. What matters is that the answer is encoded in the token, not in a designer’s memory. If line height is a percentage of size, one primitive can serve the whole ramp. If it is a fixed pixel value, each step needs its own entry.

Exporting typography tokens to code

Typography leaves Figma in two pieces, and the gap between them is where exports fail. Variables export as DTCG JSON with number and string values. Text Styles do not ride along with a variable export, because a Style is not a Variable. A complete typography token therefore needs either a separate export path for Styles or a build step that assembles the composite from the exported properties.

The DTCG specification has a composite typography type for exactly this purpose. It groups family, size, weight, line height, and letter spacing into one token, which is what a component in code actually consumes:

{
  "text": {
    "body": {
      "md": {
        "$type": "typography",
        "$value": {
          "fontFamily": "Inter",
          "fontSize": "16px",
          "fontWeight": 400,
          "lineHeight": "24px",
          "letterSpacing": "0px"
        }
      }
    }
  }
}

A CSS build, such as Style Dictionary, can emit the same shape as a class or a set of custom properties. Keeping the composite together is what lets a design system swap an entire type role in one place rather than editing five properties on a component:

.text-body-md {
  font-family: var(--font-family-base);
  font-size: var(--font-size-16);
  font-weight: var(--font-weight-400);
  line-height: var(--line-height-150);
  letter-spacing: var(--letter-spacing-0);
}

Line height is the property most likely to drift. Figma can express it as a percentage or a pixel value, while CSS accepts a unitless multiplier, pixels, or a percentage - and each behaves differently when the font size changes. Normalise the unit in the build rather than in the file, and document the choice. The same caution applies to font weight: a numeric weight and a named style such as Semibold are not interchangeable across fonts.

For the wider pipeline - modes, DTCG structure, and what a variable export preserves - see our guide to exporting Figma Variables to JSON and code. Typography adds one composite layer on top of the same process.

Real questions designers ask about typography variables

Can I use variable fonts in Figma?

Yes, and they are unrelated to Figma Variables. A variable font is a single font file that interpolates between weight, width, or optical size axes. Figma Variables are named values inside the file. A team can use both, neither, or one of the two. If the question is about switching font weight with a token, the answer is a Number Variable bound to the weight property of a text Style, not a variable font.

Are text styles the same as typography variables?

No. A text Style is a composite that applies several typography properties at once. A typography variable, as the term is usually used, is a Variable bound to one of those properties. The Style is the container; the Variables are the values inside it. You need both to get a token-driven type system.

Should every font size be a token?

Tokenize the sizes that appear more than once and carry a role. A one-off display size on a marketing page is a decision, not a system value, and turning it into a token adds maintenance without adding consistency. The test is reuse plus intent: if the value has a name a teammate would recognize and use again, it belongs in the scale.

How do I handle responsive type scales?

Put the scale in modes. One semantic collection with desktop, tablet, and mobile modes lets a single text Style change size across breakpoints without duplicate Styles. Keep the semantic names stable across modes so a component never needs to know which breakpoint it is rendering in.

Do typography variables work in code?

The values do, but they arrive separately. A variable export carries font sizes, weights, and line heights as number and string tokens. The composite - which properties belong together in text/body/md - has to come from the text Style, either through a dedicated export or a build step that reassembles it. Plan for that assembly before promising a one-file typography export.

When text styles are enough

A small product with one brand, one theme, and a stable type ramp does not need a typography variable layer. Text Styles already give consistency, and a rebrand is a one-time edit. Binding Variables adds a layer that only pays off when values need to change by mode, alias to primitives, or reach code.

The same applies to individual properties. Locking line height and letter spacing is valuable in a long-form reading interface. In a marketing hero where every headline is art-directed, binding those properties fights the work. Tokenize the system, leave the exceptions visible, and document which is which.

One signal that the layer is worth adding: more than one person edits typography, or more than one platform consumes it. At that point a text Style alone stops being a shared decision and becomes a local habit.

Final verdict - Typography Variables

Build typography tokens as text Styles with bound Variables, not as a single object Figma does not offer. Use primitive number and string Variables for the raw values, a semantic collection for the roles, and modes for responsive scales and themes. Then export the values as DTCG JSON and assemble the composite in the build. The type system is only real when the Style, the Variables, and the code all describe the same ramp.

Figma has Variables that can be bound to typography properties inside a text Style, but no Variable type that stores a whole typography style. Font family, size, weight, line height, letter spacing, paragraph spacing, and paragraph indent can each bind to a Variable. The composite remains a text Style.
Create Number Variables for sizes, weights, line heights, and letter spacing, and String Variables for font families. Then open a text Style and bind each property to the matching Variable instead of typing a raw value. The Style keeps the composite and the Variables supply the values.
A variable font is a single font file with axes such as weight or width. Typography variables are Figma Variables bound to properties of a text Style. They solve different problems and can be used together, but neither replaces the other.
Yes. A mode can change the family, weight, or size values that a text Style references. That is useful when a dark theme needs a lighter weight or a different optical size. Keep the semantic token names identical across modes so components do not need theme-specific logic.
The bound values export as number and string tokens in DTCG JSON. The composite typography token is assembled from those values, either by a dedicated Style export or by the build step. DTCG defines a typography composite type for the assembled result.
Yes when line height is a system decision, such as a 150% body rhythm or a fixed heading leading. It is the property most likely to drift between design and code because it is easy to eyeball and hard to copy. Decide the unit - percentage, pixels, or unitless - before binding it.
You can, but it removes the indirection that makes the system useful. A Style bound directly to font/size/16 cannot change by role or mode. Bind to a semantic entry such as text/body/md/size, and let that entry alias the primitive.
See all

Follow us on every platform