CSS Units: px, rem, em, % and Viewport Units
Understand absolute and relative CSS units, learn when to use px, rem, em, percentages, vw and vh, and avoid the classic em-compounding trap.
What you will learn
Nearly every size in CSS needs a unit: the px in font-size: 20px. There are many units, and choosing the right one decides whether your page adapts nicely to phones, big screens and visitors who change their font size. In this lesson you will learn what each common unit means (px, %, em, rem, vw, vh), see them side by side on one page, and get simple rules for picking one.
The HTML we will style
All the examples use this one test page. Notice the classes: each one is a “handle” that the CSS grabs.
<h1 class="page-title">Units Lab</h1>
<!-- 1. A fixed size in pixels -->
<div class="box box-px">width: 200px</div>
<!-- 2. A percentage of the parent's width -->
<div class="wrapper">
<div class="box box-percent">width: 50% of the wrapper</div>
</div>
<!-- 3. em: sizes stack up when elements are nested -->
<p class="em-1">
em level 1
<span class="em-2">em level 2
<span class="em-3">em level 3</span>
</span>
</p>
<!-- 4. rem: every level is based on the root, so no stacking -->
<p class="rem-1">
rem level 1
<span class="rem-2">rem level 2
<span class="rem-3">rem level 3</span>
</span>
</p>
<!-- 5. Viewport units -->
<div class="hero">
<h2 class="hero-title">This hero is 40vh tall</h2>
</div>
| Selector | Matches this element | Used to demonstrate |
|---|---|---|
.box |
every <div> that has the class box |
shared look (background, padding) |
.box-px |
the first .box |
a size in px |
.wrapper |
the <div> around the second box |
the parent for % |
.box-percent |
the box inside .wrapper |
a size in % |
.em-1, .em-2, .em-3 |
the paragraph and its two nested <span> elements |
em stacking up |
.rem-1, .rem-2, .rem-3 |
the second paragraph and its nested spans | rem staying steady |
.hero |
the big <div> at the bottom |
vh and vw |
Notice that some elements have two classes, like class="box box-px". The first class gives the shared look and the second gives the specific size.
Absolute vs relative units
CSS units come in two families:
- Absolute units always mean the same size.
pxis the only one you need. - Relative units are calculated from something else: a parent, the root, the font size or the screen.
%,em,rem,vwandvhare relative.
Relative units are what make a design flexible, so you will use them a lot.
px: pixels
A pixel is one dot on a screen. It is the most predictable unit: 200px is always 200 pixels wide (CSS pixels, which the browser scales on high-density screens so they stay about the same physical size).
/* the first .box */
.box-px {
width: 200px;
}
Use px for things that should not scale, such as thin borders (1px), small shadows and fine details.
Other absolute units exist (
cm,mm,in,pt), mostly designed for print. You almost never need them on the web.
%: a percentage of the parent
A percentage is calculated from the parent element.
/* the parent: exactly 400px wide */
.wrapper {
width: 400px;
background-color: #eee;
}
/* the child inside .wrapper: 50% of 400px = 200px */
.box-percent {
width: 50%;
}
If you later change the wrapper to 600px, the child follows automatically and becomes 300px. Notes:
- A percentage width is based on the parent’s width.
- A percentage height only works if the parent has an explicit height. If it does not, the percentage is ignored.
- For
font-size,150%means 1.5 times the parent’s font size.
em: relative to the font size
1em equals the current font size. It is convenient, but it has a well-known trap.
For the font-size property, em refers to the parent’s font size. Each nested level multiplies again:
/* <p class="em-1"> */
.em-1 {
font-size: 1.5em;
}
/* <span class="em-2"> inside .em-1 */
.em-2 {
font-size: 1.5em;
}
/* <span class="em-3"> inside .em-2 */
.em-3 {
font-size: 1.5em;
}
Start from the default 16px:
.em-1= 1.5 × 16 = 24px.em-2= 1.5 × 24 = 36px.em-3= 1.5 × 36 = 54px
Every level got 1.5em, yet the text grows bigger and bigger. This is called compounding, and it causes many “why is my text huge?” bugs.
For other properties such as padding, margin or width, em refers to the element’s own font size. That is useful for things that should scale with their text:
.box {
font-size: 18px;
padding: 0.5em 1em; /* 9px top and bottom, 18px left and right */
}
If you change the box’s font size, its padding scales with it. (Buttons and cards are the most common places to use this.)
rem: relative to the root
rem means “root em”: it always refers to the font size of the root element (<html>), no matter how deeply an element is nested. The default is 16px, so:
| Value | Pixels (default) |
|---|---|
1rem |
16px |
1.5rem |
24px |
2rem |
32px |
0.875rem |
14px |
0.5rem |
8px |
Now the same nested example with rem gives steady results:
/* <p class="rem-1"> */
.rem-1 {
font-size: 1.5rem;
}
/* <span class="rem-2"> inside it */
.rem-2 {
font-size: 1.5rem;
}
/* <span class="rem-3"> inside that */
.rem-3 {
font-size: 1.5rem;
}
All three levels are 24px, because each one is calculated from the root, not from its parent. Compare this with the em result above to feel the difference (use Try it on both).
Why rem is good for accessibility
Many visitors raise their browser’s default font size because it is easier on their eyes. Sizes written in rem grow with that setting. Sizes written in px stay fixed and ignore what the visitor asked for. This is the main reason professional stylesheets use rem for text and spacing.
vw and vh: the viewport
The viewport is the visible area of the page in the browser window.
1vw= 1% of the viewport width.1vh= 1% of the viewport height.
So 100vw is the full width of the window and 50vh is half its height.
/* the <div class="hero"> */
.hero {
height: 40vh;
background-color: steelblue;
color: white;
}
The hero always fills 40% of the window height, whatever the device. Some extras:
vminandvmaxare 1% of the smaller or larger viewport side.min-height: 100vhis the classic way to make a section at least as tall as the screen.- On phones,
100vhcan be taller than the visible area because of the browser’s address bar. The newer100dvh(“dynamic viewport height”) solves this. 100vwincludes the scrollbar, so a100vwelement can cause a small sideways scroll.width: 100%is usually safer for full-width blocks.
In the editor: the preview sits inside a frame, so
vhandvware measured against the preview area, not your whole screen.
Unitless values
Some properties take plain numbers, or 0, with no unit:
line-height: 1.6opacity: 0.5font-weight: 700margin: 0(zero never needs a unit,0pxis the same)
ch: a unit for reading width
One more unit worth knowing is ch, which is roughly the width of the digit “0” in the current font. It is perfect for limiting the width of text:
.wrapper {
max-width: 65ch;
}
Lines of about 60 to 75 characters are the easiest to read.
Which unit should I use?
| What you are sizing | Good choice |
|---|---|
| Font sizes | rem |
| Spacing (padding, margin, gaps) | rem (or em inside a component) |
| Thin borders, shadows, tiny details | px |
| Widths inside a parent | %, or max-width |
| Full-screen sections | vh / dvh |
| Readable text width | ch |
| Line height | a unitless number |
If you are unsure, use rem for text and spacing, % for widths, and px for borders.
Putting it together
.page-title {
font-size: 2.5rem;
margin-bottom: 1.5rem;
}
.box {
background-color: lightblue;
padding: 0.75rem;
margin-bottom: 0.75rem;
}
.wrapper {
width: 100%;
max-width: 600px;
}
.box-percent {
width: 50%;
}
.hero {
min-height: 40vh;
background-color: steelblue;
color: white;
}
Common mistakes
- Missing units:
width: 200orfont-size: 20is invalid. Only0may skip the unit. - A space between the number and the unit:
20 pxis invalid. Write20px. - Compounding
emsizes on nested elements. Useremfor font sizes to avoid it. - A percentage height with no parent height. It quietly does nothing.
- Using
pxfor all text sizes, which ignores the visitor’s font-size preference. width: 100vwcausing horizontal scroll, because it includes the scrollbar.- Using
vhfor text size, which makes text tiny on short windows and huge on tall ones. - Mixing many units randomly. Choose a small set and use it consistently.
Practice
- Click Practice in Editor. Change
.wrappertowidth: 600pxand watch.box-percentchange without editing it. - Add the
emandremrules for.em-1to.rem-3. Measure the three levels with DevTools (Computed tab) and confirm 24px, 36px, 54px, and then 24px three times. - Change the
.rem-1font size to2rem. Then change the browser’s default font size in its settings and watch theremtext grow but not thepxtext. - Give
.heroaheightof40vh, then80vh, and resize the preview to see it react. - Convert these
pxvalues torem(assume 16px):24px,12px,40px,10px. - Fix the errors:
width: 300;,font-size: 1.5 rem;,padding: 10 px;.
Recap
- CSS sizes need a unit, except for
0and unitless properties likeline-heightandopacity. pxis fixed. Use it for borders, shadows and tiny details.%is relative to the parent.emis relative to the font size and compounds when nested.remis relative to the root and stays predictable.vwandvhare percentages of the viewport width and height. Great for full-screen sections.remrespects the visitor’s font settings, so it is the best choice for text and spacing.- Next you will learn the box model, which decides how big every element really is.