CSS has more than a dozen length units, and the difference between a layout that survives contact with real users and one that breaks at the first zoom is mostly which units went where. The good news: four units do almost all the work, each has one clear job, and the rules for picking fit on a sticky note. This guide is that sticky note with the reasoning attached, and our free CSS unit converter handles the px-rem-em arithmetic whenever a design spec speaks one unit and the codebase another.
In this guide
px: fixed, and the right kind of rare
A CSS pixel is a fixed unit (and not a device pixel; high-density screens map one CSS pixel to several physical ones, which is why px values mean the same visual size on every screen). Fixed is exactly right for things that should never scale with text: borders, hairline dividers, shadow offsets, tiny decorative details. A 1px border should stay 1px when a user cranks their font size up, because it is decoration, not content. The same fixedness is exactly wrong for font sizes and content spacing, for one specific reason covered in the next section, and “px for chrome, relative units for content” is the short version of this entire article.
rem: the accessibility workhorse
One rem equals the root font size, which is 16px by default, so 1.5rem is 24px and 0.25rem is 4px. The reason rem matters is not the math but who controls that root: the user. Browsers let people set a larger default font size, separately from zoom, and font sizes declared in rem respect that setting while font sizes declared in px silently ignore it. A site typed in px overrides an accessibility preference its owner never knew existed. So: type scale in rem, content spacing in rem (a spacing system of 0.25rem steps reproduces the familiar 4-8-16-24 rhythm, generated consistently by the spacing generator), and the whole page then scales coherently when the user asks it to. Converting an existing px codebase is mechanical, every value divided by 16, which is precisely the job of the unit converter.
em: relative to the component
An em tracks the font size of the element itself (for properties like padding) or of the parent (for font-size declarations), which makes em the unit of self-scaling components: a button padded in em grows its padding proportionally when its text grows, so one button style serves small and large variants by changing only the font size. The same relativity is em’s famous trap: font sizes in em compound through nesting, a 0.9em list inside a 0.9em list rendering at 0.81 of the base and shrinking further with depth, the classic mystery of ever-smaller nested text. The working rule keeps both properties of the unit: em for padding and gaps inside a component that should track its text, rem for the font sizes themselves, and the compounding trap never fires.
vw and vh: the viewport pair
1vw is one percent of the viewport width, 1vh one percent of its height, and they bind values to the screen rather than to text or parents. Width-wise they power fluid typography, covered with clamp() below. Height-wise, the famous full-screen hero of 100vh comes with the famous mobile bug: phone browsers show and hide their address bar, the viewport height changes, and 100vh content jumps or overflows. The modern fix is the small-viewport unit svh, sized to the viewport with browser chrome visible, so 100svh fills the screen without the jump on current browsers. The pair also produces vmin and vmax, useful for elements that must fit the screen in both orientations, like a square game board sized in vmin.
The decision table, plus clamp()
| You are sizing | Use | Because |
|---|---|---|
| font sizes | rem | respects user font-size settings |
| margins, gaps, section spacing | rem | scales with the text it separates |
| padding inside buttons, badges | em | tracks the component’s own text size |
| borders, dividers, shadow offsets | px | decoration should not scale |
| hero type, full-bleed sections | clamp() with vw | fluid, with guard rails |
| full-screen heights on mobile | svh | immune to the address-bar jump |
The last piece is clamp(min, preferred, max), which turns raw viewport units into civilized ones: font-size: clamp(2rem, 5vw, 4rem) tracks the viewport (5vw is 40px on an 800-pixel screen) but never drops below 32px on phones or balloons past 64px on monitors. Bare vw type does both of those things, which is why fluid typography without clamp keeps getting written and keeps getting reverted. Unit conversions for all of the above live in the converter, and the wider toolset in the CSS generators pillar.
Frequently asked questions
Should I set html font-size to 62.5% so 1rem equals 10px?
The old trick makes rem math decimal-friendly but repurposes the very setting users adjust, and assumes the 16px default it then overrides. Current practice leaves the root alone and lets tooling or a spacing scale handle the arithmetic; the math convenience stopped being worth the side effects.
Is there any difference between 0px, 0rem and plain 0?
None in effect: zero is zero in every unit, and the unitless form is the convention. The one place a unit on zero matters is inside calc() expressions, where some calculations require typed values to stay valid.
What is the ch unit for?
One ch is the width of the font’s zero digit, which makes it the natural unit for line length: max-width: 65ch caps paragraphs near the classic 45 to 75 character measure and adapts automatically when the font changes. It is the most underused genuinely useful unit on the list.
Do percentages belong in this list?
They do, with one warning: a percentage means a fraction of the parent’s corresponding dimension, and “corresponding” varies by property, width percentages tracking parent width while padding percentages, in every direction, also track parent width. That quirk powers the old aspect-ratio padding hack and confuses everyone else; for proportional boxes today, the aspect-ratio property says what it means.