Type something to search...
How to Hand Off Typography from Figma to Developers?

How to Hand Off Typography from Figma to Developers?

The design review goes badly. The headings look heavier in the browser than in Figma, body text wraps a word earlier on every line, the spacing above buttons is off by a few pixels, and the designer is sure the developer ignored the specs. The developer copied every value from Dev Mode. Both are right. Figma and CSS describe typography with slightly different units and defaults, and a handful of those differences, like percentage letter spacing, "Auto" line height, and vertical trim, are enough to make a faithful implementation look wrong.

This article covers how Figma's text properties map to CSS, which conversions to make, how to structure text styles and variables so handoff is mostly automatic, what to put in a typography handoff document, and how designers and developers can check the result together.

How Figma Text Properties Map to CSS

Figma exposes typography through the text panel, text styles, and variables. Most properties map directly to CSS, but several need translation:

Figma propertyFigma unit / valueCSS propertyConversion
Font familyName from installed fontfont-familyAdd a fallback stack
Weight / styleName, e.g. "SemiBold"font-weight, font-styleMap name to number (see below)
Font sizepxfont-sizeDivide by 16 for rem
Line heightAuto, px, or %line-height% ÷ 100 = unitless; px ÷ size = unitless
Letter spacing% or pxletter-spacing% ÷ 100 = em
Paragraph spacingpxmargin-block-end on pConvert to em or rem
Text caseUpper, lower, titletext-transformDirect
DecorationUnderline, strikethroughtext-decorationDirect, plus offset and thickness if set
Vertical trimStandard or cap height to baselinetext-boxtext-box: trim-both cap alphabetic
Truncate text, max linesNumber of linesline-clampUse -webkit-line-clamp with fallbacks
OpenType featuresToggles in type detailsfont-variant-*, font-feature-settingsPrefer font-variant-* where it exists

The rows that cause most bugs are line height, letter spacing, and vertical trim. Each gets its own section below.

Converting Line Height

Figma supports three line-height modes:

  • Percent: 150% means 1.5 times the font size. Convert to a unitless CSS value: line-height: 1.5.
  • Pixels: 24px on 16px text. Convert by dividing by the font size: 24 ÷ 16 = 1.5.
  • Auto: uses the font's built-in vertical metrics. This maps to line-height: normal in CSS, which varies by font and sometimes by browser and operating system.

Always convert to unitless numbers. A unitless line height is inherited as a ratio, so nested elements with different font sizes stay proportional. A pixel value is inherited as a fixed length, which causes cramped or overlapping lines when a child element has larger text.

Designers should avoid Auto in production styles. It looks fine on the canvas, but because normal differs between fonts, swapping a fallback font or loading a different version of the same family changes line spacing. Set an explicit percentage on every text style. For guidance on picking the values themselves, see how to set the right line height.

Figma's line-height model distributes extra space equally above and below the text, the same half-leading model CSS uses. So once the values match, line boxes match too. That was not true in older Figma versions, which is why some long-time teams still distrust the numbers.

Converting Letter Spacing

Figma's default letter-spacing unit is percent of font size. CSS has no percent unit for letter-spacing, but em is exactly equivalent:

  • -2% in Figma becomes letter-spacing: -0.02em
  • 5% becomes 0.05em
  • 0% becomes normal or 0

If a designer used pixels instead, divide by the font size: -0.5px on 24px text is about -0.021em. Converting to em matters because tracking should scale with text size. A heading style with -0.5px that later grows to 48px on desktop would have half the intended tightening.

The most common bug here is copying letter-spacing: -2% straight into CSS. That is invalid, the browser ignores it, and the text renders with no tracking at all. If headings look looser in the browser, check for this first. Background on tracking choices is in what tracking means in typography.

Mapping Font Weights

Figma shows weights by the style names in the font file. CSS uses numbers. The standard mapping is:

Figma style nameCSS font-weight
Thin / Hairline100
ExtraLight200
Light300
Regular / Book400
Medium500
SemiBold600
Bold700
ExtraBold800
Black / Heavy900

Watch for non-standard names. Some foundries call 450 "Book" or 350 "Text". If the family is a variable font, the designer may have set an arbitrary value on the weight axis, so ask for the number, not the name.

Then confirm that every weight in the design is actually loaded on the site. If a design uses SemiBold 600 and the site only loads 400 and 700, the browser either rounds to 700 or synthesizes a fake bold from 400. Both look wrong, and the "headings are heavier in the browser" complaint usually comes from this. Add font-synthesis: none to surface missing weights during development.

Handling Vertical Trim

Figma's vertical trim setting, "Cap height to baseline", removes the space above capital letters and below the baseline inside a text box. Designers love it because it makes spacing between text and other elements match what you see. CSS line boxes include that space by default, so a 16px gap in Figma can appear as 22px in the browser.

Modern CSS can do the same thing with the text-box property:

.button-label,
.card-title {
  text-box: trim-both cap alphabetic;
}

trim-both trims the top and bottom, cap trims the top edge to cap height, and alphabetic trims the bottom edge to the baseline. Chromium-based browsers and Safari support it. Treat it as progressive enhancement: without support, spacing is slightly larger but nothing breaks.

Agree on this as a team. If the design file uses vertical trim everywhere and the code does not, every vertical measurement in the spec will be off by the half-leading. Either developers use text-box consistently, or designers measure spacing from the full line box.

Structuring Text Styles for Clean Handoff

Good handoff starts in the design file. Unstyled text with manual overrides forces developers to guess which values are intentional. A clean setup has:

  1. A small, named set of text styles. Something like Display, Heading/L, Heading/M, Heading/S, Body/L, Body/M, Label, Caption, Code. Every text layer in the file uses one.
  2. No local overrides. If a layer needs different settings, it needs a different style, or the deviation needs a comment.
  3. Variables bound to the styles. Figma variables can hold font families as strings and sizes, weights, line heights, and letter spacing as numbers. Text styles that reference variables make it obvious that two styles share one value.
  4. Responsive variants as variable modes. A Mobile and Desktop mode on the size and line-height variables tells developers exactly how each style changes across breakpoints.

Matching Style Names to Code

The single biggest handoff improvement is naming text styles exactly like their code counterparts. If Figma has Heading/L and the codebase has text-heading-lg, the mapping is trivial. If Figma has H2 Bold Large Desktop and the codebase has .title-2, every handoff is a translation exercise.

On the code side, that mapping usually lives in Tailwind theme variables or CSS custom properties:

@import "tailwindcss";

@theme {
  --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;

  --text-heading-lg: 2rem;
  --text-heading-lg--line-height: 1.2;
  --text-heading-lg--letter-spacing: -0.02em;
  --text-heading-lg--font-weight: 600;

  --text-body-md: 1rem;
  --text-body-md--line-height: 1.6;
}

@media (width >= 64rem) {
  :root {
    --text-heading-lg: 2.5rem;
  }
}

For teams that want this generated rather than hand-maintained, a token pipeline turns Figma variables into exactly this file. That approach is covered in setting up typography tokens in a design system.

Using Dev Mode Effectively

Figma's Dev Mode shows generated CSS for any selected layer, including the text style name and any variables. Use it, but do not paste blindly:

  • Look at the style name first. If a layer uses Body/M, use your existing body-md class instead of copying the raw properties.
  • Change the unit setting if available, so sizes display in rem rather than pixels.
  • Ignore absolute widths and heights on text boxes. They describe the frame, not how text should behave in a responsive layout.
  • Check for mixed styles. A text layer with a bold word inside shows multiple values. Implement that as strong inside the paragraph, not as two separate styles.

If your team uses Code Connect, map design components to real components so Dev Mode shows your component's usage instead of generated CSS.

What to Put in a Typography Handoff Document

A one-page typography spec prevents most back-and-forth. Include:

  1. Font files and licensing: the exact family names, the web font source (Google Fonts, Adobe Fonts, self-hosted files), and confirmation that the web license is in place.
  2. Fallback stack for each family.
  3. The text style table: name, family, weight, size, line height, letter spacing, and responsive changes, in code units.
  4. Measure: the maximum line length for body text, ideally 45 to 75 characters, expressed in ch or a container width.
  5. Behavior rules: where headings use text-wrap: balance, where text truncates and after how many lines, how long words and URLs break.
  6. OpenType features: tabular figures in tables, ligatures off in code, any stylistic sets.
  7. Accessibility notes: minimum sizes, contrast pairs that meet WCAG 1.4.3 (4.5:1 for normal text, 3:1 for large text), and confirmation the layout survives WCAG 1.4.12 text spacing overrides.

Here is what a style table row should look like when it reaches developers:

StyleFamilyWeightSize (mobile / desktop)Line heightTracking
heading-lgInter6002rem / 2.5rem1.2-0.02em
body-mdInter4001rem / 1.0625rem1.60
labelInter5000.875rem1.30.04em

Everything is already in CSS units. That is the point: the designer does the conversion once, in the spec, instead of each developer doing it per component.

Reviewing the Implementation Together

Figma's renderer and the browser rasterize text differently, so pixel-perfect overlays will never match exactly. Review against the things that matter:

  • Same line breaks in a paragraph at the same container width. If breaks differ, check font version, size, letter spacing, and the container width before anything else.
  • Same font file. Designers often have the desktop version of a font installed while the site loads the web version, and metrics can differ between them.
  • Same weights, with no synthesized bold or italic.
  • Same spacing between elements, with an agreed position on vertical trim.
  • Real content, including long words, translated strings, and empty states, not just the placeholder copy from the mockup.

Run the review in an actual browser at real breakpoints, with the designer and developer looking at the same screen. Ten minutes of joint review catches more than a long thread of annotated screenshots.


Figma Typography Handoff FAQ

Figma's percentage letter spacing is a percent of the font size, so divide by 100 and use em. For example, minus 2 percent becomes minus 0.02em. Never paste the percentage into CSS, because browsers ignore percent values for letter spacing.

Auto uses the font's built-in vertical metrics, which corresponds to line-height normal in CSS. The result varies by font, so it is better to set an explicit percentage in Figma and implement it as a unitless line height.

The most common reason is that the exact weight used in Figma is not loaded on the site, so the browser rounds to a heavier weight or synthesizes bold. Load every weight the design uses, and compare using the same font file version.

Use the text-box property with trim-both cap alphabetic on the elements that need it. It trims the space above cap height and below the baseline, matching Figma's cap height to baseline setting in browsers that support it.

Yes. Divide the pixel size by 16 to get rem, assuming the default root size. Rem values respect the user's browser font settings, which supports WCAG requirements for resizing text.

Yes. Figma variables can hold font family names as strings and sizes, weights, line heights, and letter spacing as numbers. Binding text styles to variables, with modes for mobile and desktop, makes responsive typography much easier to hand off.

Conclusion

Most Figma-to-code typography problems are translation problems, not skill problems. Convert line height to unitless numbers, letter spacing from percent to em, sizes from pixels to rem, and weight names to numbers. Avoid Auto line height, make sure every weight in the design is loaded on the site, and agree as a team on how vertical trim is handled, using text-box where it applies.

Then make the process repeatable. Keep a small set of text styles named exactly like their code counterparts, bind them to variables with responsive modes, write a one-page spec in CSS units, and review in a real browser together. When the design file and the codebase share names and units, handoff stops being a negotiation and becomes a lookup.

Here are some useful references for going deeper on typography handoff:

  1. Figma Help Center: Explore text properties — every text setting in Figma, including line height, letter spacing, and vertical trim.
  2. Figma Help Center: Guide to variables in Figma — variable types, modes, and binding them to text styles.
  3. MDN Web Docs: text-box — the CSS property for trimming space above and below text.
  4. MDN Web Docs: letter-spacing — valid values and why em scales with font size.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading