Text size is often the very first setting people touch in CSS, and one of the most misunderstood. Setting font-size in pixels feels like the simplest choice, but that exact choice is what quietly breaks accessibility for a share of visitors.
font-size sets the text size of an element, and that size also becomes the reference unit for anything expressed in em inside that element. Understanding how the browser computes this value prevents overflowing headings, unreadable text on mobile, and interfaces that ignore a visitor's own settings.
Definition
font-size accepts several kinds of values: a fixed size in pixels, a relative size in em or rem, a percentage, or, more recently, a fluid value computed with clamp().
h1 {
font-size: 32px;
}
p {
font-size: 1rem;
}With no CSS rule at all, every browser renders text at 16 pixels by default. That value is not arbitrary: it is the reference size every relative unit is built on, starting with rem and em.
px, rem and clamp(): three ways to size text
A pixel gives the same size everywhere, no matter the visitor's own settings. rem is computed relative to the font size of the root html element, the one a visitor can change in their browser or system settings. clamp() combines a minimum value, a preferred value that scales with screen width, and a maximum value.
| Unit | Computed relative to | Respects the visitor's settings |
|---|---|---|
px | Nothing, an absolute value | No |
rem | The font size of html | Yes |
clamp() | A range between two bounds, based on screen width | Depends on the units used inside it |
h1 {
/* Never smaller than 1.75rem, never larger than 3rem,
and a value that tracks screen width in between */
font-size: clamp(1.75rem, 4vw + 1rem, 3rem);
}This heading stays readable on a small phone, grows gradually on a tablet, then settles down on a large screen instead of scaling forever. That is the most common use of clamp(), min() and max() in modern CSS: replacing a series of Media query rules that redefine the size at each breakpoint with a single line that adapts continuously.
The accessibility trap
A visitor with low vision can ask their browser or their operating system to enlarge all text, without having to zoom in on every page they visit. That setting acts on the reference font size, so it affects everything expressed in rem or em. Text fixed in pixels stays exactly at its original size and completely ignores that setting.
Regular browser zoom, the Ctrl-plus-scroll shortcut, enlarges everything, pixels included, which hides the problem during manual testing. It is the dedicated text-size setting available on some browsers and mobile systems that reveals the gap: only that setting distinguishes relative sizes, which scale, from pixel sizes, which stay fixed.
That is exactly why most accessibility guidelines recommend expressing font-size in rem rather than pixels for body content, reserving pixels for details that genuinely need to stay fixed no matter what, like the width of a one-pixel border.
The 16-pixel baseline
Because 1rem equals 16 pixels by default, converting in your head is easy: 0.875rem is 14 pixels, 1.5rem is 24 pixels, 2rem is 32 pixels. Some teams redefine the root size, for example html { font-size: 62.5%; } to bring 1rem down to 10 pixels and simplify the mental math, but that trick has a cost: it shrinks all text on the page by 37.5 percent for a visitor who never asked for that, and it makes the code harder to read for anyone unfamiliar with the convention.
Frequently asked questions
Should pixels be banned entirely from font-size?
No, but reserve them for specific cases where a fixed size is genuinely intended, like an icon calibrated to the exact pixel. For text visitors actually read, rem and em remains the recommended choice, precisely because it respects their display preferences.
What is the difference between em and rem for a text size?
em is computed relative to the font size of the nearest parent element, which can create a compounding effect when several nested levels all use em. rem is always computed relative to the root html element, which gives a more predictable result in a complex layout.
Why does my site look smaller after changing the root font size?
If html gets a font size below 16 pixels, every rem value on the site shrinks along with it, including ones that seemed unrelated to text, like spacing or icon sizes defined in rem for consistency.
Does clamp() fully replace media queries for text size?
It replaces most simple cases of text that grows with the screen, but a Media query is still useful for bigger changes, like rearranging an entire layout or switching an aspect ratio at a specific breakpoint.