Sizing in CSS: px vs em vs rem
We have been conversant with using pixels for sizing in CSS, but why use em or rem? When building accessible websites, you need to consider inclusion. When you use px, you don't put user preferences at the forefront.
When the user zooms in or change the browser font setting, websites need to adjust to fit the user's setting. px
do not scale but em
and rem
scales.
Sequel to this, setting the font size of the html element in percentage is recommended. Assuming the browser font size is set to 16px (i.e. the default), setting the font size of the html (root) element to 62.5% will default 1rem to 10px.
You may have used em
and rem
CSS units for a while but the difference between the two still appears vague. In this article, I will discuss the difference and when to use a particular unit to avoid building websites with unproportionate sizing.
The Difference
Pixel (px
) is a commonly used CSS unit on websites. px
is not scalable, it is an absolute unit. Change in the value of another element does not affect the value of absolute units. The value assigned is fixed irrespective of the user setting.
Element (em
) and Root element (rem
) are responsive units interpreted into equivalent px
unit by the browser. They are relative units. Change in the value of the parent or root element affects the value of relative units. They scale with the device. So, what makes the two different? The difference lies in how the values are derived by the browser. To view the computed values, open the Chrome Developer Tools and navigate to the Computed tab.
The computed pixel value of em
unit is relative to the font size of the element being styled. This is also affected by inherited values from the parent elements unless it is explicitly overridden by a px
unit which is not subject to inheritance.
The computed pixel value of rem
unit is relative to the font size of the root (html) element. This is however affected by the font size setting on the browser as a result of inheritance unless it is overridden by a px
unit which is not subject to inheritance.
Using em
With em
unit, the computed pixel value is derived from the font size of the styled element while putting the inherited (parent and grandparent) values into consideration. Using em
unit can turn out to be complex. em
units should be used to allow for scalability within the context of a specific design element e.g. setting the padding, margin and line-height of menu items to use em
values works well to properly scale navigation menu items.
Unit = Font size of the element being styled x Specified value of the element being styled
Given a section
element with a font size of 14px
, when you create a rule, with the padding property set to 3em
, it would evaluate to 42px
(14 x 3 = 42). This is irrespective of the font size assigned to the html element. Simply put, the font size of the element on which em unit is declared determines the pixel unit value.
/* style */
html {
font-size: 18px;
}
section {
font-size: 14px;
padding: 3em;
}
/* computed style */
html {
font-size: 18px;
}
section {
font-size: 14px;
padding-bottom: 42px;
padding-left: 42px;
padding-right: 42px;
padding-top: 42px;
}
Using rem
When you use rem
, the px
equivalence depends on the font size of the html element.
Unit = Font size of the root html element x Specified value of the element being styled
Given a web page with its root element assigned a font size of 18px
, and the margin property assigned a value of 2rem
. The computed value for the margin would evaluate to 36px
(18 x 2 = 36).
/* style */
html {
font-size: 18px;
margin: 2rem;
}
/* computed style */
html {
font-size: 18px;
margin-bottom: 36px;
margin-left: 36px;
margin-right: 36px;
margin-top: 36px;
}
The Effect of Inheritance on em Unit
When inheritance is applied to em
CSS units, the method of calculating the equivalent pixel varies.
With em
, the child element inherits the value of its parent and up to the grandparent. This alters the supposed pixel value as the inherited value is put into consideration.
Unit = Font size of the element being styled x Font size of the parent and grandparent elements (if any) x Specified value of the element being styled
<section class="page-wrapper">
Grandparent
<div class="container">
Parent
<div class="container">
Child
</div>
</div>
</section>
/* style */
html {
font-size: 16px;
}
.page-wrapper {
font-size: 1.5em;
}
.container {
padding: 2em;
}
To calculate the padding
value of the .container
class above, inheritance is put into consideration.
/* computed style */
html {
font-size: 16px;
}
.page-wrapper {
font-size: 24px;
}
.container {
padding-bottom: 48px;
padding-left: 48px;
padding-right: 48px;
padding-top: 48px;
}
The section having page-wrapper
class inherits the font size of the parent (html) element (16px). It multiplies that value by the font size (1.5em) which computes to 24px (16 x 1.5 = 24px).
Apparently, the value of the padding specified on the container
class will inherit from the parent element. Its padding value will now be computed to 48px (24 x 2 = 48).
If the container
class specifies a font size in em
unit, the calculation will be extended further to use that value. e.g If a font size of 1.5em
is specified on the container
class,
/* style */
html {
font-size: 16px;
}
.page-wrapper {
font-size: 1.5em;
}
.container {
font-size: 1.5em;
padding: 2em;
}
the padding value will be computed as:
This will equate to 57.6px (16 x 1.5 x 1.5 x 2 = 72).
/* style */
html {
font-size: 16px;
}
.page-wrapper {
font-size: 24px;
}
.container {
font-size: 36px;
padding-bottom: 72px;
padding-left: 72px;
padding-right: 72px;
padding-top: 72px;
}
To override this inherited value, you need to explicitly set the unit of the element being styled to
px
.
For instance,
<section class="page-wrapper">
Grandparent
<div class="container">
Parent
<div class="container">
Child
</div>
</div>
</section>
With style:
html {
font-size: 16px;
}
.page-wrapper {
font-size: 24px;
}
.container {
font-size: 1.5em;
}
Note that the font size value of the div
with container class above is computed based on its parent element, not the root. The interesting thing here is that the Parent and Child in the HTML above do not have the same font size irrespective of the fact that they share the same class name. The font size of Child is computed based on its parent i.e Parent and the font size of Parent is computed based on its parent i.e Grandparent.
It is computed as:
<section class="page-wrapper">
Grandparent (24px)
<div class="container">
Parent (24 x 1.5 = 36px)
<div class="container">
Child (36 x 1.5 = 54px)
</div>
</div>
</section>
While using em
, it is advisable to define the font size on the html
element instead of declaring it explicitly on other elements.
The Effect of Browser Settings
The font size set on the browser by the user can affect the value when CSS rem
unit is used within a web application. This affects em
units also as a result of inheritance.
When no font value is set on html element
Given the example below,
/* style */
html {
box-sizing: border-box;
}
.page-wrapper {
font-size: 2rem;
}
the font size of the element with the class name of page-wrapper
will depend on the browser settings since no font size is set on the root element. By default, the browser font size is set to 16px
and inherited by the root element. If a user alters the font size of the browser through the settings, the page would be rendered to conform to the settings. Assuming the user sets the default font size to 14px, the computed font size value for page-wrapper
will equate to 28px (14 x 2 = 28).
When em or rem font value is set on html element
When an em
or rem
font value is set on the html element with a custom browser setting, the computed pixel value will be a multiple of the browser font size setting with the specified font size.
For instance, if a website's html element has a font-size property set to 1.5em, and the browser font size set to 14px, the computed pixel value of the root element would be 1.5 x 14 = 21px.
The Recommended Setting
Setting the font size of the html element in percentage is recommended. This solves the problem.
Assuming the browser font size is set to 16px (i.e. the default), setting the font size of the root html element to 100% will default 1rem to 16px. This is still not the optimal solution. A better approach will be to use 62.5%. This will equate 1rem to 10px. Starting with this as the base simplifies the calculations.
* {
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
body {
margin: 0;
font-size: 1.6rem; /* 16px */
}
.page-header {
font-size: 3.2rem; /* 32px */
}
For browsers that do not support the usage of rem, you can use SCSS mixin or provide a fallback.
* {
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
body {
margin: 0;
font-size: 16px;
font-size: 1.6rem;
}
.page-header {
font-size: 32px;
font-size: 3.2rem;
}
Final notes
- Use preprocessors to abstract the calculations using a px to rem converter function
- Use
em
only for sizing that needs to scale based on the font size of an element other than the html (root) element. - Use
rem
unit for elements that scale depending on a user's browser font size settings. Userem
as the unit for most of your property value. - For complex layout arrangement, use percentage (%).
You may want to learn more about writing better CSS in a codebase