Units
Everything you see on the web is composed out of the little dots of light that make up your device’s screen: pixels. So, when measuring out the artefacts that make up our interfaces, thinking in terms of pixels, and using the CSS px
unit, makes sense. Or does it?
Screens’ pixel geometries vary wildly, and most modern displays employ sub-pixel rendering, which is the manipulation of the color components of individual pixels, to smooth jagged edges and produce a higher perceived resolution. The notion of 1px
is fuzzier than how it’s often portrayed.
Screen resolutions—how many pixels screens pack—also differ. Consequently, while one “CSS pixel” (1px
in CSS) may approximate one “device” or “hardware” pixel on a lower resolution screen, a high resolution screen may proffer multiple device pixels for each 1px
of CSS. So there are pixels, and then there are pixels of pixels.
Suffice it to say that, while screens are indeed made up of pixels, pixels are not regular, immutable, or constant. A 400px
box viewed by a user browsing zoomed in is simply not 400px
in CSS pixels. It may not have been 400px
in device pixels even before they activated zoom.
Working with the px
unit in CSS is not incorrect as such; you won’t see any error messages. But it encourages us to labour under a false premise: that pixel perfection is both attainable and desirable.
Scaling and accessibility
Designing using the px
unit doesn’t only encourage us to adopt the wrong mindset: there are manifest limitations as well. For one, when you set your fonts using px
, browsers assume you want to fix the fonts at that size. Accordingly, the font size chosen by the user in their browser settings is disregarded.
With modern browsers now supporting full page zoom (where everything, including text is zoomed proportionately), this is often blown off as a solved problem. However, as Evan Minto discovered, there are more users who adjust their default font size in browser settings than there are users of the browsers Edge or Internet Explorer. That is: disregarding users who adjust their default font size is as impactful as disregarding whole browsers.
The units em
, rem
, ch
, and ex
present no such problem because they are all units relative to the user’s default font size, as set in their operating system and/or browser. Browsers translate values using these units into pixels, of course, but in such a way that’s sensitive to context and configuration. Relative units are arbitrators.
Relativity
Browsers and operating systems typically only afford users the ability to adapt the base or body font size. This can be expressed as 1rem
: exactly one times the root font size. Your paragraph elements should always be 1rem
, because they represent body text. You don’t need to set 1rem
explicitly, because it’s the default value.
:root {
/* ↓ redundant */
font-size: 1rem;
}
p {
/* ↓ also redundant */
font-size: 1rem;
}
Elements, like headings, should be set relatively larger — otherwise hierarchy will be lost. My <h2>
might be 2.5rem
, for example.
h2 {
/* ↓ 2.5 × the root font-size */
font-size: 2.5rem;
}
While the units em
, rem
, ch
, and ex
are all measurements of text, they can of course be applied to the margin
, padding
, and border
properties (among others). It’s just that text is the basis of the web medium, and these units are a convenient and constant reminder of this fact. Learn to extrapolate your layouts from your text’s intrinsic dimensions and your designs will be beautiful.
Needless conversion
A lot of folks busy themselves converting between rem
and px
, making sure each rem
value they use equates to a whole pixel value. For example, if the base size is 16px
, 2.4375rem
would be 39px
, but 2.43rem
would be 38.88px
.
There’s no need to do precise conversion, since browsers employ sub-pixel rendering and/or rounding to even things out automatically. It’s less verbose to use simple fractions like 1.25rem
, 1.5rem
, 1.75rem
, etc — or to let calc()
do the heavy lifting in your Modular scale.
Proportionality and maintainability
My <h2>
is 2.5 times the root/base size. If I enlarge the root size, my <h2>
—and all the other dimensions set in rem
-based multiples—will be enlarged proportionately. The upshot is that scaling the entire interface is trivial:
@media (min-width: 960px) {
:root {
/* ↓ Upscale by 25% at 960px */
font-size: 125%;
}
}
If I had instead adopted px
, the implications for maintenance would be clear: the lack of relative and proportional sizing would require adjusting individual elements case-by-case.
h3 {
font-size: 32px;
}
h2 {
font-size: 40px;
}
@media (min-width: 960px) {
h3 {
font-size: 40px;
}
h2 {
font-size: 48px;
}
/* etc etc ad nauseum */
}
Viewport units
In Every Layout, we eschew width-based @media
queries. They represent the hard coding of layout reconfigurations, and are not sensitive to the immediate available space actually afforded the element or component in question. Scaling the interface at a discrete breakpoint, as in the last example, is arbitrary. What’s so special about 960px
? Can we really say the smaller size is acceptable at 959px
?
Viewport units are relative to the browser viewport’s size. For example, 1vw
is equal to 1% of the screen’s width, and 1vh
is equal to 1% of the screen’s height. Using viewport units and calc()
we can create an algorithm whereby dimensions are scaled proportionately, but from a minimum value.
:root {
font-size: calc(1rem + 0.5vw);
}
The 1rem
part of the equation ensures the font-size
never drops below the default (system/browser/user defined) value. That is, 1rem + 0vw
is 1rem
.
The em
unit
The em
unit is to the rem
unit what a container query is to a @media
query. It pertains to the immediate context rather than the outer document. If I wanted to slightly enlarge a <strong>
element’s font-size
within my <h2>
, I could use em
units:
h2 {
font-size: 2.5rem;
}
h2 strong {
font-size: 1.125em;
}
The <strong>
’s font-size
is now 1.125 × 2.5rem
, or 2.53125rem
. If I set a rem
value for the <strong>
instead, it wouldn’t scale with its parent <h2>
: if I changed the h2
value, I would have to change the h2 strong
CSS value as well.
As a rule of thumb, em
units are better for sizing inline elements, and rem
units are better for block elements. SVG icons are perfect candidates for em
-based sizing, since they either accompany or supplant text.
The ch
and ex
units
The ch
and ex
units pertain to the (approximate) width and height of one character respectively. 1ch
is based on the width of a 0
, and 1ex
is equal to the height of your font’s x
character—also known as the x height or corpus size.
In the Axioms section, the ch
unit is used to restrict elements’ measure for readability. Since measure is a question of characters per line, ch
(short for character) is the only appropriate unit for this styling task.
An <h2>
and an <h3>
can have different font-size
values, but the same (maximum) measure.
h2,
h3 {
max-inline-size: 60ch;
}
h3 {
font-size: 2rem;
}
h2 {
font-size: 2.5rem;
}
The width, in pixels, of one full line of text is extrapolated from the relationship between the rem
-based font-size
and ch
-based max-width
. By delegating an algorithm to determine this value—rather than hard coding it as a px
-based width
—we avoid frequent and serious error. In CSS layout terms, an error is malformed or obscured content: data loss for human beings.