CSS Units Explained: px, em, rem, %, vw and vh
Basics4 minInnovialabs editorial team
In short
px is a fixed size, rem follows the root font, em follows the current or parent font, % follows the parent box and vw/vh follow the viewport; rem works best for fonts and spacing, and dvh or svh for full-screen blocks on mobile.
The short answer
CSS has absolute units that ignore their surroundings and relative units that are calculated from something else: the font, the parent or the browser window.
| Unit | Relative to | Good for |
|---|---|---|
px | A fixed CSS pixel | Borders, shadows, fine details |
rem | Font size of the root html element | Fonts, spacing, component sizes |
em | Font size of the current element (for font-size, the parent) | Padding inside buttons, icons next to text |
% | Size of the parent box | Column widths, fluid blocks |
vw / vh | 1% of viewport width / height | Full-screen sections, fluid typography |
Note that a CSS pixel is not a physical screen dot. On high-density screens one px spans several physical pixels, so 16px looks roughly the same on a laptop and a phone.
rem vs em and how em compounds
Browsers usually default the root font to 16px, so 1rem = 16px. If a user increases the font size in browser settings, every rem value grows with it — a real accessibility benefit. Sizes in px ignore that setting.
rem is always calculated from one place, html. em follows the nearest context, so nested values multiply:
.menu { font-size: 1.2em; }
<ul class="menu"> <!-- 16 × 1.2 = 19.2px -->
<li>
<ul class="menu"> <!-- 19.2 × 1.2 = 23.04px -->
<li>Item</li>
</ul>
</li>
</ul>
Each nested level gets bigger — a common cause of "mysteriously huge" text. With 1.2rem every level would be the same.
Still, em is useful where size should follow the text: padding: 0.5em 1em on a button keeps its proportions when you change its font-size.
Percentages: relative to what exactly
% is almost always calculated from the parent box, with a few catches:
width: 50%— half of the parent's width.height: 50%only works if the parent has a defined height.paddingandmarginin percent are calculated from the parent's width, even the vertical ones.font-size: 120%equals1.2em.
vw, vh and the 100vh problem on mobile
1vw is 1% of the viewport width, 1vh is 1% of its height. It seems height: 100vh gives exactly one screen. On desktop it does, but on mobile browsers the address bar and toolbars appear and hide. vh usually equals the viewport height with the bars hidden, so when the address bar is visible the bottom of the block is cut off and a button at the bottom of the screen cannot be seen.
Newer units fix this:
svh— the small viewport height, with all bars visible. The block always fits.lvh— the large height, with the bars hidden (like the oldvh).dvh— the dynamic height, which changes as the bars move.
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100svh; /* reliably fits on mobile */
}
dvh is more precise, but the block may resize while scrolling and cause jumps. For a hero section svh is the usual choice; dvh suits full-screen modals.
One more trap: on desktop 100vw includes the scrollbar width, so width: 100vw can create horizontal scrolling. For full width, width: 100% is usually enough.
A practical rule of thumb
- Fonts —
rem. For headings that grow with the screen, useclamp():font-size: clamp(2rem, 5vw, 4rem). - Spacing and component sizes —
rem; padding that should track the text —em. - Layout —
%,frin grid, flexbox; fixedpxwidths only asmax-width. - Borders, shadows, hairlines —
px. - Screen height —
svhordvhwith avhfallback.
Common mistakes
- Setting
font-sizeinemon nested components and getting compounding. - A fixed
pxheight on a block of text — when the font grows, the text overflows. 100vhfor a mobile hero section withoutsvh.- Setting the root size to
62.5%for "convenient" rem math and forgetting it breaks third-party components.