ToolPal
Code on a computer screen

CSS Units Converter: Stop Guessing and Start Converting

πŸ“· Christina Morillo / Pexels

CSS Units Converter: Stop Guessing and Start Converting

A practical guide to px, rem, em, vh, vw, and every other CSS unit β€” when to use each, how to convert between them, and how to avoid the em inheritance trap.

April 9, 202610 min read

The Handoff Problem Nobody Talks About

Picture this: a designer sends you a Figma file. Every measurement is in pixels β€” 24px font size, 16px padding, 48px margin. You copy those values into your CSS. The layout looks right on your machine.

Then a colleague who bumps their browser's base font size to 20px for readability loads the page. Your carefully specified 24px heading stays at exactly 24px while everything around it shifts. The carefully balanced hierarchy that worked at 16px base falls apart. The designer is confused. You're confused. Nobody wins.

This isn't a rare edge case. It's what happens when you treat every CSS measurement as an absolute value when most of them should be relative. The fix is understanding which unit to reach for and when β€” and having a reliable way to convert between them.

That's exactly what the CSS Units Converter is built for. But before we get to the tool, let's build the mental model from scratch.

The Units Glossary

CSS has more measurement units than most developers realize. Here's what each one actually is:

px β€” Pixels

The pixel is the baseline most of us learned CSS with. 1px is nominally one pixel on a standard-density screen. On high-DPI (retina) displays it gets scaled by the device pixel ratio, so 1px might actually render as 2 or 3 physical pixels β€” but the browser handles that math, not you.

The key property of px: it is fixed. It doesn't respond to user preferences, parent elements, or anything else in the document. This is sometimes exactly what you want (a 1px border should always be 1px) and sometimes exactly the problem (a 16px font size ignores a user who needs larger text).

rem β€” Root Em

rem stands for "root em." One rem equals the font size of the <html> element β€” typically 16px in most browsers by default, but crucially, it respects whatever the user or your CSS sets that root size to.

If a user sets their browser's default font size to 20px, 1rem is 20px everywhere on the page. Your entire layout scales up proportionally. That's a significant accessibility win.

1.5rem at 16px base = 24px. Same ratio, different base, predictable everywhere.

em β€” Em (the slippery one)

em is similar to rem but relative to the current element's font size, not the root. This sounds fine until you start nesting elements.

If an element has font-size: 1.2em and it's inside a parent with font-size: 1.2em, the child ends up at 1.2 Γ— 1.2 = 1.44em relative to the root. Stack three levels deep and you have 1.728em. This compounding effect is the em inheritance trap β€” more on that in a dedicated section below.

vh / vw β€” Viewport Height and Width

1vh = 1% of the browser viewport's height. 1vw = 1% of the viewport's width. These are genuinely useful for full-screen sections and fluid typography that adapts to screen size.

The caveat: on mobile, the browser's address bar is sometimes visible and sometimes hidden. This changes the viewport height, which makes 100vh elements jump in height as the user scrolls. The CSS Working Group added dvh (dynamic viewport height) to handle this, but browser support is still catching up.

% β€” Percent

Percent is context-dependent in a way that trips up even experienced developers. width: 50% means 50% of the parent's width. font-size: 120% means 120% of the inherited font size (behaves like em). margin-top: 10% is 10% of the containing block's width β€” not height, which surprises most people the first time.

pt, cm, mm β€” Print Units

These are the physical measurement units. They map to real-world dimensions at a standard screen resolution but are almost exclusively useful for print stylesheets. 1pt = 1/72 of an inch. 1cm = 10mm. You'll rarely write these in a web stylesheet unless you're formatting something for printing.

The Base Font Size Mental Model

Here's the thing most tutorials skip: browsers don't just have a default font size, they have a user-configurable default font size. Chrome, Firefox, and Safari all let users set this in their preferences. Historically, the default across browsers has been 16px, and that's what most conversion tables assume.

The math that follows:

1rem = 16px  (at browser default)
0.75rem = 12px
1.25rem = 20px
1.5rem = 24px
2rem = 32px

Some design systems set html { font-size: 62.5%; } to make 1rem = 10px, which makes the arithmetic easier (1.6rem = 16px). I've used this trick and I'll admit it works β€” but it silently breaks components that assume the browser default, and it doesn't actually improve accessibility. If you inherit a codebase using this trick, check for it early.

The bottom line: rem is predictable because it always anchors to one value. That's why it's the right choice for most typographic and spacing decisions.

Decision Framework: Which Unit for What

This is the part of every CSS article where the advice gets generic. I'll try to be concrete instead.

Font sizes β†’ rem

Always. There's very rarely a good reason to set font-size in px. If someone changes their browser's default, you want your text hierarchy to stay intact. rem does this for free.

body { font-size: 1rem; }       /* 16px at default */
h1   { font-size: 2.5rem; }     /* 40px at default */
h2   { font-size: 2rem; }       /* 32px at default */
small { font-size: 0.875rem; }  /* 14px at default */

Component spacing β†’ rem or px depending on intent

Padding and margin on components is a judgment call. If the spacing is meant to scale with text (like the vertical padding on a button), use rem. If it's a fixed visual gap that shouldn't grow (like the gap between icon and text inside a button), px is fine.

.button {
  padding: 0.75rem 1.5rem;  /* scales with font size */
  border: 1px solid currentColor;  /* always 1px */
  border-radius: 4px;  /* fixed visual property */
}

Full-screen and viewport-relative layouts β†’ vh/vw

Hero sections, modals that should take a percentage of the screen, sticky sidebars β€” these belong in viewport units.

.hero {
  min-height: 100vh;  /* full-screen section */
  width: 100vw;
}

Just watch the mobile address bar issue. If you're building something where pixel-perfect full-screen behavior matters on mobile, dvh is worth the polyfill cost.

If you have a @media print section, switch to physical units there. Printers think in inches and points, not screen pixels.

@media print {
  body { font-size: 12pt; }
  .page-break { page-break-after: always; }
}

"Sized to content" β†’ percentage or auto

For widths that should respond to their container, % is often the right call. Just remember the mobile quirk with margin percentages being width-based.

The Em Inheritance Trap (With a Real Example)

Let me show you exactly why em in font sizes gets dangerous.

Say you're building a card component with some nested text:

.card { font-size: 1.2em; }
.card .meta { font-size: 0.9em; }
.card .meta .timestamp { font-size: 0.85em; }

If the parent of .card has font-size: 16px (1rem), here's what you actually get:

  • .card β†’ 16px Γ— 1.2 = 19.2px
  • .card .meta β†’ 19.2px Γ— 0.9 = 17.28px
  • .card .meta .timestamp β†’ 17.28px Γ— 0.85 = 14.688px

None of those numbers are what you probably intended. You were probably thinking "a little smaller than normal for meta, even smaller for timestamps." What you got instead is a compounding mess that's hard to debug without a calculator.

The same styles with rem instead:

.card { font-size: 1.2rem; }   /* 19.2px */
.card .meta { font-size: 0.9rem; }  /* 14.4px */
.card .meta .timestamp { font-size: 0.85rem; }  /* 13.6px */

These numbers correspond directly to the root font size. No compounding. Much more predictable.

The one place em is genuinely useful: when you want something to scale relative to the element's own font size, like line-height: 1.5em (1.5Γ— the element's font size) or padding: 0.5em on a button where the padding should grow proportionally if the button's font size is changed.

Conversion Reference Table (16px Base)

pxremem (no inheritance)pt
10px0.625rem0.625em7.5pt
12px0.75rem0.75em9pt
14px0.875rem0.875em10.5pt
16px1rem1em12pt
18px1.125rem1.125em13.5pt
20px1.25rem1.25em15pt
24px1.5rem1.5em18pt
32px2rem2em24pt
40px2.5rem2.5em30pt
48px3rem3em36pt

These assume 1rem = 16px. If your root font size is different, every conversion changes. That's precisely why the tool exists.

How the CSS Units Converter Works

The CSS Units Converter handles a problem that a static table can't: what if your base font size isn't 16px? What if your viewport is 1440px wide and you need exact vw values?

The tool lets you:

  1. Enter any numeric value
  2. Select the source unit (px, rem, em, vh, vw, %, pt, cm, mm)
  3. Set your base font size (defaults to 16px, adjustable)
  4. Set your viewport dimensions (for accurate vh/vw conversions)
  5. See all equivalent values instantly β€” no need to calculate each one manually

This is the workflow I use when translating Figma specs (which are always in px) into rem values for a design system. Enter the px value, read the rem equivalent, done. It's also useful for sanity-checking when you get a layout that's slightly off β€” plug in the px value from DevTools and compare it to what the rem should be.

One edge case the tool handles gracefully: em values displayed are relative to the configured base font size (i.e., assuming no inheritance). If you have inheritance in play, you'll need to account for the compounding yourself β€” or just switch to rem.

Quick Tips for Tailwind CSS Users

Tailwind shows pixel values in its documentation (e.g., text-xl is described as 1.25rem / 1.75rem but class names sometimes list pixel equivalents). A few things worth knowing:

Tailwind uses rem internally. text-base is 1rem, text-lg is 1.125rem, text-xl is 1.25rem. If you're setting font-size manually to match a Tailwind class, use rem β€” not the pixel number from the docs.

The spacing scale is also rem. p-4 is 1rem (16px at default). p-8 is 2rem (32px). When your designer says "24px padding," that's p-6 in Tailwind (1.5rem). Our converter helps bridge the gap when you need to match specific design values to Tailwind classes.

Custom base font sizes work fine with Tailwind. If you set html { font-size: 18px }, all Tailwind rem values recalculate automatically. Run the converter with base 18px to double-check your custom values.

Wrapping Up

CSS unit choice isn't trivia β€” it's the difference between a layout that adapts gracefully and one that breaks for a subset of your users. The rules of thumb:

  • px for fixed visual properties (borders, outlines, specific decorative elements)
  • rem for font sizes and scalable spacing β€” your accessibility default
  • em sparingly, where you want proportional scaling to the element's own size
  • vh/vw for viewport-relative layouts, with dvh for mobile full-screen accuracy
  • % for container-relative sizing, keeping the width-based margin quirk in mind
  • pt/cm/mm only in print stylesheets

And when you need to convert between any of these, the CSS Units Converter has you covered β€” with adjustable base size and viewport dimensions so your conversions match your actual project, not a textbook example.

The goal is less time doing mental math in your head and more time building things that actually work.

Frequently Asked Questions

Share this article

XLinkedIn

Related Posts