
How to Hand Off Typography from Figma to Developers?
- Sajjad
- Typography
- 01 Oct, 2026
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 property | Figma unit / value | CSS property | Conversion |
|---|---|---|---|
| Font family | Name from installed font | font-family | Add a fallback stack |
| Weight / style | Name, e.g. "SemiBold" | font-weight, font-style | Map name to number (see below) |
| Font size | px | font-size | Divide by 16 for rem |
| Line height | Auto, px, or % | line-height | % ÷ 100 = unitless; px ÷ size = unitless |
| Letter spacing | % or px | letter-spacing | % ÷ 100 = em |
| Paragraph spacing | px | margin-block-end on p | Convert to em or rem |
| Text case | Upper, lower, title | text-transform | Direct |
| Decoration | Underline, strikethrough | text-decoration | Direct, plus offset and thickness if set |
| Vertical trim | Standard or cap height to baseline | text-box | text-box: trim-both cap alphabetic |
| Truncate text, max lines | Number of lines | line-clamp | Use -webkit-line-clamp with fallbacks |
| OpenType features | Toggles in type details | font-variant-*, font-feature-settings | Prefer 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:
24pxon 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: normalin 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 becomesletter-spacing: -0.02em5%becomes0.05em0%becomesnormalor0
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 name | CSS font-weight |
|---|---|
| Thin / Hairline | 100 |
| ExtraLight | 200 |
| Light | 300 |
| Regular / Book | 400 |
| Medium | 500 |
| SemiBold | 600 |
| Bold | 700 |
| ExtraBold | 800 |
| Black / Heavy | 900 |
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:
- 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. - No local overrides. If a layer needs different settings, it needs a different style, or the deviation needs a comment.
- 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.
- Responsive variants as variable modes. A
MobileandDesktopmode 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 existingbody-mdclass instead of copying the raw properties. - Change the unit setting if available, so sizes display in
remrather 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
stronginside 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:
- 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.
- Fallback stack for each family.
- The text style table: name, family, weight, size, line height, letter spacing, and responsive changes, in code units.
- Measure: the maximum line length for body text, ideally 45 to 75 characters, expressed in
chor a container width. - Behavior rules: where headings use
text-wrap: balance, where text truncates and after how many lines, how long words and URLs break. - OpenType features: tabular figures in tables, ligatures off in code, any stylistic sets.
- 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:
| Style | Family | Weight | Size (mobile / desktop) | Line height | Tracking |
|---|---|---|---|---|---|
heading-lg | Inter | 600 | 2rem / 2.5rem | 1.2 | -0.02em |
body-md | Inter | 400 | 1rem / 1.0625rem | 1.6 | 0 |
label | Inter | 500 | 0.875rem | 1.3 | 0.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:
- Figma Help Center: Explore text properties — every text setting in Figma, including line height, letter spacing, and vertical trim.
- Figma Help Center: Guide to variables in Figma — variable types, modes, and binding them to text styles.
- MDN Web Docs: text-box — the CSS property for trimming space above and below text.
- MDN Web Docs: letter-spacing — valid values and why
emscales with font size.


