Line length, leading and the reading column
Measure, line height and size depend on each other. How I set a reading column in CSS, why 65ch is not 65 characters, and what changes on a phone.

When a long article feels tiring, the cause is usually the column, not the typeface. Lines that run too long make the eye work to find the start of the next one. Lines that run too short break phrases apart and turn reading into a series of small jumps. Leading that is too tight makes the lines blur together; too loose and the paragraph falls apart into stripes.
These three things, measure, leading and size, have to be set together. Change one and the other two usually need to move.
How long a line should be
The old print guidance is 45 to 75 characters per line, with about 66 as a comfortable middle for a single column of continuous text. It holds up well on screens. Below 45 the text gets choppy; above 80 or so, readers start losing their place on the return sweep.
In CSS the obvious tool is the ch unit:
.prose {
max-width: 65ch;
}
It works, with one catch. 1ch is the width of the zero glyph in the current font, and zero is wider than an average lowercase letter in most text faces. So 65ch usually gives you something like 70 to 80 real characters per line, depending on the face. For a serif with narrow lowercase, I often end up at 60ch or even 58ch to get a true 66. Count a few lines of real text once, then adjust.
Put the limit on the text block itself, not on the page container. The container can be as wide as the layout needs for images, tables and pull quotes. Only the paragraphs need the narrow measure.
Leading follows the measure
Line height is not a fixed number. Longer lines need more of it, because the eye has further to travel back and needs a clearer channel to follow. Larger x-heights need more too, because the lowercase fills more of the line and the gaps between lines look smaller.
As a starting point for body text:
- around 45 characters: 1.4
- around 65 characters: 1.5 to 1.6
- around 80 characters: 1.65 or more
Then adjust by eye for the face. Unitless values are the right choice here, because they scale with the element’s own font size instead of being computed once and inherited as a fixed length.
.prose {
font-size: var(--step-0);
line-height: 1.55;
max-width: 62ch;
}
.prose h2 {
line-height: 1.15; /* headings are short lines; they want less */
}
Space between paragraphs
Once leading is set, paragraph spacing should be a clear step above it, or paragraphs will not read as units. I use one full line of space (margin-block-start: 1lh where supported, or 1.5em as a fallback that roughly matches). Space before a heading should be larger than the space after it, so the heading visibly belongs to the text it introduces. Twice as much above as below is a fine default.
Indented first lines are the other traditional option, and they work well on screens for long fiction or essays. Pick one. Indents and spacing together are redundant.
Small screens change the maths
On a 360px phone with 16px side margins, you have 328px of column. At 18px that holds roughly 40 to 45 characters of a typical serif. That is the bottom of the comfortable range, which is why body text on phones often wants to be slightly smaller than on desktop, not larger. 17px is frequently the better number there.
The other adjustment is leading: with shorter lines, you can tighten it a little. A fluid size with clamp() and one breakpoint for the line height keeps the relationship steady across widths.
.prose {
font-size: clamp(1.0625rem, 1.02rem + 0.19vw, 1.1875rem);
line-height: 1.45;
}
@media (min-width: 48em) {
.prose { line-height: 1.58; }
}
You cannot put a unitless line height inside clamp() next to a vw term; a number and a length do not add. You could make the line height a length instead, but then it stops scaling with the element’s own font size. A media query or two is less elegant and more predictable.
Rag, widows and hyphens
Left-aligned text with a ragged right edge is the right default on the web. Justified text without good hyphenation opens rivers of white space, and browser hyphenation varies by language and engine.
Two newer properties help the rag. text-wrap: pretty asks the browser to avoid a single short word on the last line of a paragraph, at a small layout cost; I use it on body text. text-wrap: balance evens out line lengths across a block, which is good for headings and bad for paragraphs, because it makes every line shorter than it needs to be.
hyphens: auto with a correct lang attribute is worth turning on for narrow columns in languages with long words, and worth leaving off for English body text at a normal measure, where it adds more breaks than it removes.
Check it by reading
None of these numbers replace reading a real article in the column. Set one, on the device people will use, and read the whole thing. If you notice the layout at any point, something is off. The measure is right when you stop thinking about it.