Adamarant
Start
Back to Field notes

Optimal line length and line height: 6 steps to set both

Product DesignSep 11, 20268 min read

Body text reads best at 50 to 75 characters per line. Here is how to set measure and leading in CSS, with the numbers, the WCAG floor, and real browser support.

A copy of The Hindu newspaper featuring articles and photographs on a white surface

By the end of this you will have two numbers you can defend in a design review: a maximum width for body text, and a line-height tuned to that width. Both written in CSS that survives a font swap, a zoom to 200 percent, and a phone held in portrait.

The short answer first. Body text reads best between 50 and 75 characters per line, with 66 as the most-cited target, and the leading that suits that band sits between 1.4 and 1.6 as a unitless multiplier. Typographers call the line width the measure. Everything below is how to reach those numbers without guessing.

Measure is the most ignored setting in product UI. Most layouts set a container in pixels or percent and let the text fall where it lands. On a 27-inch monitor a paragraph in a full-width container runs past 140 characters. The eye finishes a line, sweeps back to the left, and lands on the wrong one. The reader rereads, then skims, then leaves. Baymard's review of the readability literature puts the usable band at 50 to 75 characters. Emil Ruder, working in print decades before the browser existed, landed on 50 to 60.

What you need before you start

  • The production font, not a placeholder. Measure depends on the width of the glyphs you actually ship.
  • A type scale already settled. Setting leading before the body size is fixed means doing the work twice.
  • A real content sample: the longest paragraph in the product, not lorem ipsum.
  • Browser dev tools and ten minutes.

Step 1: Count the characters you ship today

Open the longest paragraph in the product at your widest breakpoint. Select one full line, copy it, paste it into anything that counts characters. That number is your current measure. Repeat at 1280px and at 390px.

Three numbers come back, and the wide one is usually the shock: past 100 characters on a large desktop, something reasonable on a laptop, something under 40 on a phone. Write all three down. They are the before state, and they are what you will check against in the verification step.

Step 2: Cap body text with the ch unit

.prose p { max-width: 65ch; }

The ch unit is the advance width of the digit zero in the current font. It is not a character count. In most proportional typefaces the zero is wider than the average lowercase letter, so a 65ch column carries meaningfully more than 65 characters of running prose. Set the cap, then go back to step 1 and count again. If the line comes out at 80, drop to 58ch or 60ch and recount. Two passes is normal, and the final number depends on the typeface, which is why copying a value from another project rarely lands.

Cap the paragraph, not the wrapper. A 65ch limit on the container squeezes the tables, code blocks, diagrams and full-bleed images inside it, all of which want the width the paragraph is giving up. Scope the cap to the text elements and let the rest of the layout breathe.

Step 3: Write line-height as a unitless number

Use a number, never a pixel value.

body { font-size: 16px; line-height: 1.55; }

A unitless value is inherited as a multiplier, so every child recomputes it against its own font-size. A pixel value is inherited as the computed figure: a 13px caption nested inside a 16px block inherits 24px of leading and reads as double-spaced. MDN documents the unitless form as the recommended practice for exactly this reason, and it also scales correctly when the page is zoomed.

Then tune the number to the measure, because measure and leading are one decision, not two. A long line needs more vertical separation for the eye to find the start of the next one. A short line needs less, or the paragraph falls apart into stripes. The working range we use:

  • 45 to 55 characters per line: 1.4 to 1.5
  • 55 to 65 characters per line: 1.5 to 1.6
  • 65 to 75 characters per line: 1.6 to 1.7

Below 1.5 on a wide measure, the return sweep starts failing. The WCAG 1.4.8 Visual Presentation criterion asks for at least space-and-a-half within paragraphs, which is where the 1.5 floor for body copy comes from.

Step 4: Give headings less leading than body text

Headings run tighter: 1.1 to 1.3. The reason is arithmetic. Leading is a multiple of font-size, so a 40px headline at 1.6 opens a 64px gap between its two lines, wide enough that the reader parses them as two separate elements rather than one sentence. At 1.15 the same headline holds together.

This is where a type scale pays off. If your scale is a set of steps, attach a leading value to each step rather than to each component. Display steps get 1.1, headings 1.2, body 1.55, captions 1.4. One decision per step, applied everywhere.

Step 5: Fix the mobile end of the range

On a 390px phone, 16px body text lands around 35 to 40 characters per line. Baymard's guidance for small screens is 30 to 50, so that is fine. The failure on mobile is the opposite of desktop: padding stacked on padding drives the count under 30, the text starts breaking every four words, and the ragged right edge turns into a sawtooth.

Do not shrink body text to fit more characters on a phone. 16px is the floor. Below it, iOS Safari zooms the viewport whenever a form input is focused, which breaks the layout in a way nobody attributes to typography. Reduce horizontal padding instead, or lower the base size of the surrounding chrome and leave the prose alone.

Step 6: Clean the ragged edge

Two CSS properties finish the job, and one tempting option is a trap.

text-wrap: balance evens out line widths across a short block. Use it on headings and on anything two or three lines long. Chromium limits it to six wrapped lines for performance, so it is not a body-text tool. Support reached Baseline in 2024: Chrome and Edge 114, Firefox 121, Safari 17.5.

text-wrap: pretty is the body-text counterpart. It adjusts the last lines of a paragraph to avoid an orphan, a single word sitting alone on the final line. WebKit shipped its implementation in Safari 26, Chrome and Edge have had it since 117, and Firefox is still the gap. It degrades to normal wrapping where it is missing, so it costs nothing to add.

The trap is text-align: justify. Justified text produces a clean right edge and, without hyphenation and a proper line-breaking engine, rivers of white space running down the column. WCAG 1.4.8 rules it out explicitly for blocks of text.

How to verify it works

  1. Recount at three widths. Same method as step 1, at your widest breakpoint, at 1280px, and at 390px. All three should land inside 45 to 75, and the mobile one inside 30 to 50.
  2. Zoom to 200 percent. The text should reflow without horizontal scrolling. This is the fifth requirement of WCAG 1.4.8, and a measure set in ch passes it for free where one set in pixels often does not.
  3. Check paragraph separation. 1.4.8 asks for paragraph spacing at least 1.5 times the line spacing. At 16px and 1.55 leading, line spacing is about 25px, so paragraphs want roughly 38px between them.
  4. Swap the font. Change the body family to something notably wider or narrower and reload. If the measure holds near the same character count, the ch cap is doing its job.

One caveat on the standard: 1.4.8 is Level AAA, and it is written as "a mechanism is available" rather than as a hard default. Treat it as a target for the reading surface, not as a pass-fail gate on every screen in the product.

Common failures and fixes

Captions look double-spaced. Someone set line-height: 24px on a parent. The computed pixel value is inherited by smaller children. Convert every line-height in the codebase to a unitless number.

The measure vanishes inside a two-column layout. A max-width: 65ch on the paragraph is a maximum, not a minimum. In a narrow column the paragraph shrinks below 45 characters and the rhythm breaks. Set a minimum column width, or collapse to one column earlier.

Images and tables got narrow. The cap went on the container instead of the text elements. Move it to p, li, blockquote and the heading levels that need it.

A URL or a token blows past the cap. Long unbroken strings ignore the measure and force horizontal overflow. Add overflow-wrap: break-word to the prose container.

The numbers are right and it still reads wrong. Check the other two variables before touching measure again: contrast between the text and the background, and the spacing above each heading. A heading sitting equidistant between the paragraph above and the paragraph below attaches itself to the wrong one.

Going further

Measure and leading are the reading layer. The two adjacent decisions are the sizes themselves, covered in typographic scale for product UI, and the spacing around the text, covered in optical alignment in UI. If the page still reads flat after all three, the problem is usually hierarchy, not typography: visual hierarchy without color covers the tools that work when the accent color is doing nothing.

Sources

Photo by Ashni on Unsplash

Frequently asked questions

Is 65ch the same as 65 characters per line?+

No. The ch unit is the advance width of the digit zero in the active font, not an average character. In most proportional typefaces the zero is wider than the average lowercase letter, so a 65ch column usually fits more than 65 characters of running prose. Set the cap, then copy one rendered line and count it. If the result lands above 75, reduce the cap and count again.

Does line length still matter on mobile?+

Yes, but the risk flips. On desktop the danger is lines that run too long; on a phone it is lines that run too short. A 390px viewport with 16px text lands around 35 to 40 characters, which sits inside the 30 to 50 band Baymard recommends for small screens. The fix when the count drops under 30 is to cut horizontal padding, not to shrink the type. 16px is the practical floor, because iOS Safari zooms the viewport on focused inputs below it.

Does WCAG require a maximum line length?+

Success Criterion 1.4.8 sets 80 characters as the ceiling for blocks of text, 40 for Chinese, Japanese and Korean. It is Level AAA and it is worded as "a mechanism is available", so it is not a pass-fail gate on every screen. Treat 80 as an accessibility ceiling and 66 as the typographic target. The same criterion also asks for line spacing of at least 1.5 inside paragraphs, paragraph spacing 1.5 times larger than that, no text justified to both margins, and no horizontal scrolling at 200 percent zoom.

Should line-height ever be set in pixels or rem?+

Only for a single fixed element you fully control, and even then the unitless number is safer. A pixel or rem line-height is inherited as a computed length, so a smaller child element keeps the parent’s leading and looks double-spaced. A unitless value is inherited as a ratio and recomputes against each element’s own font-size. The one place a fixed value earns its keep is vertical rhythm on a strict baseline grid, which almost no product UI actually enforces.

Studio

Start a project.

We write about what we build. Tell us what you want to build.