// typography · line_length_reading_constraint
Line Length Is a Reading Constraint, Not a Layout Afterthought
Most layouts treat line length as whatever happens to fit between the margins at whatever width the visitor's browser happens to be. It isn't neutral: stretch a line past roughly 75 characters and the eye's return sweep to the start of the next line grows long enough to lose its place; squeeze it under about 45 and reading breaks into a choppy, hyphen-heavy rhythm before it's found one at all. The measure is a real ergonomic constraint with a known comfortable range, a CSS unit built specifically to hold it, and a line-height that has to move together with it — not a layout afterthought decided by whichever container happens to be nearby.
§ 01
Why Wide Text Blocks Feel Exhausting to Read
Reading a line of text is a sequence of short jumps — saccades — separated by brief fixations, followed by one larger jump at the end of the line: the return sweep back to the left margin and down to the next line. That return sweep is the fragile part of the sequence. It's a ballistic eye movement, planned and fired before the eye actually knows where it will land, and its accuracy depends heavily on how far it has to travel. On a very long line, the return sweep has to cross most of the width of the page, and imprecise landings become common — the eye either re-reads the line it just finished or drops onto the wrong line below it, and the reader has to hunt to recover their place.
A very short line has the opposite problem. The eye barely finishes accelerating into a line before it has to break, so a narrow-measure column produces a rapid, choppy rhythm of returns, and the line-breaking algorithm — whichever browser or app is wrapping the text — has to work harder to avoid orphaned words or excessive hyphenation, since there's so little room to work with on each line.
Both failure modes come from the same variable: the number of characters on a line, not the pixel width of the container holding it. A phone in portrait orientation and a widescreen monitor can both hold a comfortable measure or an uncomfortable one — what matters is how many characters actually land between the margins, which is a function of font-size and container width together, not either one alone.
§ 02
The 45–75 Character Measure
Typography settled on a working answer to "how many characters is too many" long before screens existed: a comfortable line for continuous reading runs somewhere between about 45 and 75 characters, including spaces, with the middle of that range — commonly cited as somewhere around 60 to 66 — treated as close to ideal for long-form body text. The range isn't an arbitrary round number; it reflects roughly how much text a reader can hold in view and carry into a return sweep without the sweep's accuracy breaking down at one end or the rhythm turning choppy at the other.
The convention traveled from print to the web largely intact, though on-screen text tends to sit toward the lower half of the range more often than print does — screens are read from a wider range of distances, under more variable lighting, and often on devices with lower effective resolution than a printed page, so overshooting toward 75+ characters costs more on screen than the equivalent overshoot would in print. Sitting closer to 60–65 characters is a safer default for a general-purpose website than reaching for the theoretical upper bound of 75.
§ 03
Measuring in ch, Not Pixels or Percentages
CSS has no unit that means "characters per line" directly, but
ch is the closest available proxy: one
ch equals the width of the "0" (zero) glyph in
whatever font and font-size are currently active. Because that
width is derived from the font's own metrics instead of an
absolute pixel count, max-width: 65ch continues
to approximate the same character count even when the
font-size changes underneath it — from a user's browser zoom,
from an OS-level accessibility text-scaling setting, or from a
fallback font loading in before the intended one finishes
downloading. A pixel-based max-width has no equivalent
defense: 600px holds a wildly different number of
characters depending on font-size, and it holds exactly the
same, now-wrong, number of characters after the font-size
changes, because px was never tracking character count in the
first place.
ch isn't a perfect character count either — it's
calibrated to one specific glyph, and most letters in a
proportional typeface are narrower or wider than the "0", so
65ch of a typical paragraph usually renders as somewhat fewer
than a literal 65 characters. That's fine: the goal was never
an exact count, it was a measure that stays inside the
comfortable range as conditions change, and ch is
the only sizing unit built to track the two variables — font
and font-size — that actually determine how many characters
fit.
Percentage widths fail the same test in the opposite direction: a percentage is relative to the parent container, which has no relationship to the font's metrics at all, so a 60%-wide column can land at a wildly different character count depending on viewport size and font-size simultaneously, with nothing correcting for either.
§ 04
Why Line-Height Has to Scale With the Measure
Measure and line-height aren't independent settings, even though most CSS resets treat them that way with one global line-height value applied everywhere. The wider a line is, the further the return sweep from section one has to travel, and the more vertical separation it needs between lines to land on the correct one instead of the line above or below it. A line-height that reads as perfectly comfortable on a narrow column can feel measurably too tight the moment the same text block widens — not because the number changed, but because the return sweep it has to support got longer.
The relationship runs in the direction most people don't expect at first: narrow columns can carry a tighter line-height, not a looser one. With little horizontal distance to travel, the eye's return sweep on a 40–45ch column stays accurate even with the lines packed closely — something in the neighborhood of 1.2 to 1.3 unitless line-height reads as connected rather than cramped. Stretch the same text out to 70–90ch and that same tight spacing starts producing exactly the mis-landings section one described; something closer to 1.5–1.65 gives the longer sweep the vertical room it needs.
§ 05
A Formula That Keeps Measure and Leading in Sync
There's no single physically correct mapping from measure to line-height — the right amount depends on the typeface's x-height, its own default vertical rhythm, and the reading context — but the direction of the relationship is fixed: leading should climb as the measure widens, and it should never sit at one fixed number applied identically to every column width on the page. A simple linear model, tuned by eye rather than derived from any external standard, is enough to keep the two moving together instead of drifting out of sync as a layout's column widths change:
leading ≈ 1.2 + (measure − 45) × 0.01 · clamped to 1.2–1.65
Run a few widths through it and the shape becomes intuitive: 45ch — the tight end of the comfortable range — lands at 1.2. 65ch, a common default body-copy width, lands at 1.4. 75ch, the wide end of the comfortable range, lands at 1.5, which happens to line up with the minimum line-height accessibility guidelines expect users to be able to reach without breaking the layout, covered in the reference table below. Push the measure past that into genuinely too-wide territory and the formula clamps at 1.65 rather than climbing indefinitely — past a certain width, more leading stops compensating for the problem, and the real fix is capping the measure itself, which is exactly what the next section covers.
§ 06
Holding the Measure at Any Viewport
Because ch already scales with font-size, and
font-size itself can scale fluidly with viewport width using
clamp() — the same technique covered in
the companion guide on type scales
— pairing the two keeps a column's measure roughly anchored
across breakpoints without writing a separate override for
every screen width. The custom-property pattern below keeps
measure and leading traveling together as a single unit per
component, instead of two settings that can be edited
independently and quietly drift apart:
:root {
--measure: 65ch;
--leading: 1.4;
}
.prose {
max-width: var(--measure);
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
line-height: var(--leading);
}
/* A narrower column earns a tighter leading, not the same 1.4 */
.prose--sidebar {
--measure: 42ch;
--leading: 1.25;
}
/* A wider, full-bleed column earns a looser leading */
.prose--feature {
--measure: 78ch;
--leading: 1.55;
}
The real risk sits almost entirely at the wide end of the
viewport range. Narrow viewports solve the measure problem for
free — a phone screen is rarely wide enough to hold 90
characters at a readable font-size no matter what the CSS
says, so mobile rarely needs explicit protection. A wide
desktop monitor or a maximized browser window is the opposite
case: without an explicit
max-width: Nch cap on the reading column, a
proportional-width container has nothing stopping it from
stretching well past 75–90 characters, and that's the scenario
the ch-based cap exists to prevent.
§ 07
Four Pitfalls That Quietly Break the Measure
-
01
Capping width in px instead of ch. A max-width set in pixels holds a fixed pixel count, not a fixed character count — the moment font-size changes, from browser zoom, an OS text-scaling setting, or a font substitution, the actual number of characters per line silently drifts out of the comfortable range while the CSS itself never changed.
-
02
One global line-height for every column width. A single site-wide
line-height: 1.5rule feels like a safe default, but section four's relationship means it can't be correct for every column at once — it under-serves a 90ch feature paragraph and over-serves a 40ch sidebar callout, since neither was the width the number was actually chosen for. -
03
Applying the measure cap to headings and short UI strings. The 45–75 character guidance describes continuous body copy specifically — the text a reader's eye actually sweeps through line after line. Wrapping a large display headline, a button label, or a form field's helper text to the same 65ch container it doesn't need produces awkward, unpredictably ragged line breaks on strings that were never long enough to need a measure limit in the first place.
-
04
Assuming ch translates directly across scripts and typefaces.
chis calibrated to the "0" glyph of whatever font is active, and that glyph's proportion to the script's average character width isn't constant — it varies across Latin typefaces and doesn't map onto non-Latin scripts the same way at all. CJK characters, for instance, run roughly twice as wide as Latin ones on average, which is exactly why the accessibility guideline in the reference table below sets a separate, lower character ceiling for CJK text instead of reusing the Latin number.
§ 08
Quick Reference
| Concept | Typical value | Note |
|---|---|---|
| Comfortable measure | 45–75 characters | ~60–66 often cited as the sweet spot |
| Narrow-column leading | ~1.2–1.3 | Short return sweep, tight spacing reads fine |
| Wide-column leading | ~1.5–1.65 | Longer return sweep needs the extra room |
| Sizing unit | ch | Tracks font-size; px and % do not |
| WCAG text spacing (AA, 1.4.12) | Line height ≥ 1.5× font size | Users must be able to reach this without breaking layout |
| WCAG visual presentation (AAA, 1.4.8) | Width ≤ 80 characters (40 for CJK) | Advisory ceiling, just above the 45–75 comfort range |
| Applies to | Body copy, continuous text | Not headings, labels, or short UI strings |
None of this depends on hitting an exact character count — 63 versus 67 was never the meaningful difference. What matters is treating the measure as a real constraint that a layout either respects or ignores: capped with a unit that actually tracks characters instead of pixels, paired with a line-height chosen for that specific width rather than borrowed from a global reset, and left alone on the short strings — headings, labels, buttons — that were never part of the problem it solves. Get those three things right once, in a couple of reusable custom properties, and the measure holds itself steady across every breakpoint and every column width the layout ever needs, without a fresh decision required each time one gets added.