Structure is right, but the visuals look off? Closing the last few pixels between Adobe XD and Figma!

The very last thing that lingers during an XD-to-Figma migration is that nagging feeling of “the structure is right, but somehow the visuals look off.” 🤔 (For the “structural” side of conversion — Auto Layout and Components — see “Skip the XD-stack rebuild — drop your layouts straight into Figma Auto Layout!” and “Faithfully recreate your XD components — drop them straight into Figma!” for the details!)

Gradient angles drift a little. Text gets clipped at a frame’s edge. Text inside an Auto Layout sits a few pixels lower than it should — these are rooted in fundamental rendering differences between XD and Figma, and no amount of structural copying will address them.

Most converters stop here and say “the structure came over, so we’re done.” But for anyone actually doing the migration, the post-conversion cleanup is where most of the time goes 😇

Pixel Fine Converter takes on this “last few pixels” problem, one case at a time! We can’t promise a perfect recreation, but we’ve built in four fine-tuning passes specifically designed — with XD and Figma’s rendering differences in mind — to cut down the amount of post-conversion cleanup you have to do 🔧

Note: Fine-tuning is a Pro plan ($29, one-time purchase) feature. The plugin itself is free to install, and the Free plan still handles structure, styles, groups, and other basic conversions.

🧩 Why fine-tuning is needed

XD and Figma are both vector-based design tools, but their internal rendering logic differs significantly.

AreaXDFigma
Gradient coordsNormalized (0–1) space, independent of element sizeBounding-box space — aspect ratio affects the angle
Font metricsIts own text-layout engineCan occupy roughly 1.15–1.45× the space for the same font
Single-line lineHeightIgnored when rendering (no visible effect)Always applied (can cause clipping if too small)
Half-leadingBaked directly into the single-line text’s Y(lineHeight - natural height) / 2 distributed evenly above and below the glyph

Structure conversion alone can’t fix any of this. Pixel Fine Converter’s fine-tuning is the dedicated step that applies a counter-measure to each of these differences so you have less cleanup afterwards! We can’t perfectly correct everything, but we apply what we can, knowing why the mismatch happens in the first place.

Below we dig into each of the four features — what the problem is, why it happens, and how we resolve it!

🎨 Gradient angle correction

Problem

XD defines gradients in a normalized (0–1) coordinate space, so the angle stays the same regardless of element size. Figma uses bounding-box space, so on non-square elements, diagonal gradient angles drift.

This becomes very obvious on elements with extreme aspect ratios. On a 960×50px header bar with a 45° gradient, XD renders the intended diagonal, but Figma ends up drawing something much closer to horizontal — a visible mismatch.

Why the drift happens

XD computes the gradient direction in pixel space. “Top-left to bottom-right” is the same angle whether your element is square or wide. Figma samples in a normalized [0,1]² space; on a wide element the horizontal axis is “compressed,” so a 45° spec actually renders at a much more horizontal angle.

So the same (0,0) → (1,1) spec means “a diagonal line in pixel space” in XD but “a diagonal line in normalized space” in Figma — and on non-square elements those aren’t the same angle.

Solution

Pixel Fine Converter recomputes the affine transform matrix with the element’s aspect ratio (W/H) factored in. We re-project XD’s normalized coordinates into Figma’s pixel space to correct the angle!

Concretely: we expand the gradient’s start/end points into pixel space and build a transform weighted by W² and H². The result is adjusted so that, when sampled by Figma, the angle matches what XD drew. Unglamorous, but effective 💪

Note that this correction applies only to linear gradients. Radial gradients use a different conversion path (composing and inverting transform matrices).

Auto-skip

The correction is automatically skipped in these cases:

  • Square elements — if width and height differ by 0.01px or less, the two spaces are effectively identical
  • Horizontal / vertical gradients — angles that aren’t affected by aspect ratio

Trade-off

The correction is ON by default, but in rare cases it can make things worse. If the post-conversion gradient looks off, you can switch it OFF in the options to revert to the naive behavior.

✂️ Preventing text clipping

Problem

XD and Figma use different font metrics. For the same font and size, Figma can occupy roughly 1.15–1.45× the space. Text that fit neatly inside a frame in XD can get clipped on the right or bottom edge in Figma.

This is especially noticeable inside clipping frames (clipsContent=true). The overflow is only 1–3 pixels — but in a clipping frame those few pixels show up as visible cut-off text. A few pixels may not sound like much, but you notice them 😅

Why the rendered size differs

Font files (TTF/OTF) store multiple sets of metrics that determine line height — most prominently the sTypo (OS/2 table) and hhea (hhea table) systems.

XD and Figma read different metric systems, so the same font file produces different line heights in each. The size of the difference varies by font, but it’s especially large for CJK fonts (Yu Gothic, Noto Sans JP, etc.): while Latin fonts are around 1.15–1.25× larger, CJK fonts can be around 1.45×. That’s why Japanese-heavy designs tend to surface the issue first!

Solution

When this option is ON, we compute the actual dimensions of every text node inside clipping frames, and when overflow is detected, we extend the frame by just enough pixels.

  • 🔍 Detect: check whether each text node’s right/bottom edge exceeds the frame
  • 📐 Measure: find the maximum overflow across children and round up to the pixel
  • 📦 Extend: expand frame and mask shape together — works whether the mask is rectangular, elliptical, or a vector

Rather than applying a fixed padding, we compute overflow per child so no wasted empty space is introduced 🎯

Scope and exclusions

This feature targets text nodes with textAutoResize = WIDTH_AND_HEIGHT (point text). Text box–type (fixed-width) text wraps to the box width, so it’s out of scope. Rotated clipping frames are skipped because we can’t guarantee overflow detection accuracy.

Trade-off

Frame sizes end up slightly larger than the original XD dimensions. If you need pixel-perfect layout, you may still need to tweak by hand.

📏 Line-height normalization

Problem

In XD, if lineHeight is smaller than fontSize, single-line text is still displayed in full — XD doesn’t use this value when rendering single-line text.

Figma always applies lineHeight to the text box’s height. If lineHeight < fontSize, the text box gets shorter than the glyph and the top and bottom get clipped. A 72px heading with a 24px lineHeight ends up mostly cut off in Figma 💦

Why lineHeight < fontSize happens in the first place

You might wonder who sets such a weird value in XD. It commonly happens when a designer sets lineHeight for multi-line text and later changes the text to a single line. Because XD doesn’t visibly apply that value, the old number just stays in the file — and no one notices. Classic XD!

Solution

When this option is ON, we clamp lineHeight to be at least the natural line height that Figma actually measures (naturalLineHeight). Rather than just comparing to fontSize, we load the font in Figma, measure the real rendered height, and use that as the floor.

This way the correction reflects per-font metrics accurately — roughly fontSize × 1.45 for CJK fonts, roughly fontSize × 1.2 for Latin fonts.

⚡ Solving the half-leading problem

Problem

This is the trickiest of the fine-tuning features!

XD’s single-line text ignores lineHeight for positioning. Figma, on the other hand, distributes (lineHeight - natural height) / 2 evenly above and below the glyph (half-leading).

The difference is especially visible inside Auto Layout frames. Imagine a button with a 24px heading and a 14px sub-label side by side. In XD they sit neatly aligned to the top; in Figma each one gets its own half-leading on top, and baselines end up a few pixels off from each other.

Coordinate contamination — making it worse

To make matters worse, XD’s coordinate data itself can already be “contaminated” by lineHeight. In textAutoResize=WIDTH_AND_HEIGHT mode, XD sometimes bakes a lineHeight offset into the Y coordinate of the text.

Since lineHeight has no visible effect in XD, designers don’t notice. But when the conversion uses those coordinates as-is and then Figma adds half-leading on top, you get two layers of drift stacked on top of each other (“XD coord drift + Figma half-leading drift”). The complexity is impressive, in the worst possible way 😵‍💫

Solution

Pixel Fine Converter applies different normalization strategies depending on the Auto Layout direction:

DirectionActionWhy
Horizontal Auto LayoutReset lineHeight to AUTOSide-by-side text cares about glyph centering; AUTO produces a natural visual alignment
Vertical Auto LayoutBack-compute an appropriate height from XD’s child coordinate deltasWhen the coordinate data is trustworthy, we can restore XD’s intended spacing

Vertical Auto Layout: detecting coordinate contamination

For the vertical case, Pixel Fine Converter back-computes “the height XD intended” from the Y-coordinate deltas between child nodes. We take the ratio between the computed XD height and the text’s actual Figma height, and use that ratio to judge how trustworthy the coordinates are:

  • 📊 Ratio ≤ 1.5 → Coordinates are trustworthy. Use the XD-derived height as lineHeight
  • 📊 Ratio > 1.5 → Coordinates are likely contaminated by lineHeight. Fall back to lineHeight=AUTO

Why 1.5? Normal font-metric differences between XD and Figma fall in the 1.0–1.3× range. A ratio greater than 1.5 can’t be explained by metrics alone, which strongly suggests the coordinate itself was influenced by lineHeight.

It doesn’t work perfectly in every case, but it reduces half-leading drift noticeably for most real-world layouts 🛡️

Controlling the scope

Half-leading normalization has three modes to match how aggressive you want to be:

  • 🔴 OFF — No normalization (default)
  • 🟡 Auto-layout children only — Normalize only text inside Auto Layout frames (recommended)
  • 🟢 All — Normalize every eligible text node (if you want to be aggressive about it)

The reason “Auto-layout children only” is the default is that inside regular frames, lineHeight is sometimes used deliberately for vertical centering — for example button text centered vertically via lineHeight. Normalizing would break that.

Trade-off

Normalized text loses its original lineHeight value. If you later switch to multi-line text, the line spacing will use Figma’s default rather than XD’s original value.

⚠️ Limits and scope (being upfront about it)

We’ve walked through the four fine-tuning passes in detail, but they’re all mitigations for a fundamental problem: XD and Figma render differently. They won’t work perfectly in every case, so here are the trade-offs in plain language:

  • Gradient angle correction can rarely make things worse. If the post-conversion gradient looks off, turn it OFF and reconvert
  • Text clipping prevention changes frame sizes, which is a trade-off against pixel-perfect layouts
  • Line-height normalization overwrites the original lineHeight. If you later change the text to multi-line, you’ll want to reset the spacing
  • Half-leading normalization defaults to only Auto Layout children. You can apply it to regular frames too by turning “Auto-layout children only” off, but expect more unintended changes
  • Overflow detection on rotated clipping frames is skipped
  • The full option behavior is documented in Guide: Fine-tuning option reference

You can’t fully eliminate rendering-engine differences at the level of principle, but “if the plugin can absorb 80–90% of the irritating drift, the remaining 10–20% of cleanup is a lot lighter” — that’s the philosophy we develop to. We’ll keep working to make the plugin one you can genuinely rely on.

🚀 Try the Free plan first to check conversion quality

Pixel Fine Converter is free to install from the Figma Community! The Free plan includes the full set of basic conversions — shapes, text, styles, groups, masks, images, transforms — so you can check the conversion quality on your own .xd file right away.

For fine-tuning and the other detail-oriented conversion features, you can unlock them with the Pro plan ($29, one-time purchase — no subscriptions) 🎁

🚀 Install on Figma (Free)

One-click install from Figma Community