Why CSS Has So Many Units
CSS offers a surprisingly wide range of units for specifying sizes: px, em, rem, %, vw, vh, vmin, vmax, ch, ex, cm, mm, in, pt, and pc. This variety exists because different sizing problems call for fundamentally different approaches. A font size that should scale with the user's browser preferences needs a different unit than a 1-pixel border that should always stay crisp.
Understanding which unit to reach for in a given situation is one of the most practical skills in front-end development.
Absolute Units: Fixed Sizes
**px (pixels)** is the most commonly used absolute unit in CSS. Despite its name, a CSS pixel is not necessarily a physical device pixel — on high-DPI (retina) displays, a CSS pixel might correspond to 2 or 3 physical pixels. What px provides is a consistent, predictable size that does not change based on context.
Use px for: borders (typically 1px), box shadows, very small decorative details, and any measurement where exact pixel-perfect sizing is required regardless of context.
Avoid px for font sizes and layout dimensions in most cases — it prevents users from scaling text in their browser accessibility settings.
Other absolute units like cm, mm, in, pt, and pc are intended for print stylesheets. Avoid them for screen layouts.
Relative to Font: em
**em** is relative to the font size of the element it is applied to. If an element has a font size of 16px, then 1em = 16px, 1.5em = 24px, and 0.75em = 12px.
When em is used for font-size itself, it compounds. A child element set to 1.2em inside a parent also set to 1.2em results in 1.2 × 1.2 = 1.44 times the base size. This compounding can cause unexpected results in deeply nested structures.
Use em for: padding and margins on components where you want them to scale proportionally with the component's text. Navigation items that should feel balanced relative to their label font size benefit from em-based padding.
Relative to Root: rem
**rem** (root em) is relative to the font size of the root `<html>` element, not the current element. This eliminates the compounding problem of em.
The default browser font size is 16px, so 1rem = 16px unless the user or your stylesheet changes the root font size.
Use rem for: almost all font sizes. This respects the user's browser font size preferences — if someone has set their browser to 20px base, your rem-based text scales up accordingly, making your site more accessible. Also use rem for spacing values (margins, padding, gaps) at a page-wide scale.
A common pattern: set the root font size to 62.5% (which makes 1rem = 10px at default browser settings) to make rem math more intuitive. However, this can interfere with user font size preferences, so use with care.
Percentage: Relative to Parent
**%** is relative to the same property on the parent element. For width, 50% means half the parent's width. For font-size, 120% means 1.2 times the parent's font size.
The parent-relative behavior makes % very useful for fluid layouts. A column set to width: 33.333% inside a flex or grid container naturally adapts to any container width.
One trap: percentage heights only work if the parent has an explicit height defined. Setting height: 50% on a div inside a parent with height: auto results in the browser ignoring the percentage.
Viewport Units: Relative to the Screen
**vw** (viewport width) and **vh** (viewport height) are percentages of the viewport dimensions. 100vw is the full width of the browser window, 50vh is half its height.
These are particularly powerful for full-screen sections, hero images, and sticky elements. A section with height: 100vh always fills exactly one screen.
However, vh has a well-known problem on mobile browsers: the browser chrome (address bar, navigation) is not consistently included in the viewport height calculation, causing "100vh" to be taller than the visible area. Modern CSS provides `dvh` (dynamic viewport height) as a solution — `100dvh` accounts for the browser chrome.
**vmin** is the smaller of vw and vh. **vmax** is the larger. These are useful for sizing elements that should feel proportional regardless of device orientation.
Newer Units Worth Knowing
**ch** is the width of the "0" character in the current font. Useful for setting the max-width of text content — `max-width: 70ch` creates a readable line length regardless of font size.
**clamp()** is a function rather than a unit, but worth mentioning: `clamp(1rem, 2.5vw, 2rem)` sets a minimum, preferred, and maximum value in a single declaration, enabling fluid type scaling without media queries.
Practical Rules of Thumb
Use **rem** for font sizes and global spacing. Use **em** for component-internal spacing that should scale with text. Use **%** for fluid layout widths. Use **px** only for thin borders, shadows, and small decorative details. Use **vw/vh** (or dvh) for full-viewport sections and sticky elements. Avoid px for font sizes to preserve accessibility.