text-wrap: balance
On two different ragging problems that need two different values, 500 years of typesetters caring about this, and the four workarounds that were all worse.
A three-up card grid. At most widths the headings break sensibly. At about 900 pixels, one of them breaks as five words and then “surveys,” alone, on the second line, and it looks like a mistake because it is one.
I’ve fixed that with a non-breaking space typed between the last two words. I’ve fixed it with a <br> inside a media query. I’ve fixed it by asking a client to rewrite a heading. I’ve installed a JavaScript library that walked the DOM inserting non-breaking spaces.
There are two properties for it now, they do different jobs, and between them they cover essentially everything.
Two different problems
The reason there are two values is that ragging goes wrong in two distinct ways.
In a short block, a heading or a caption, the problem is uneven lines. Six words on the first line and one on the second, or a long line followed by a stub. What you want is for the lines to come out roughly the same length, even if that means fewer words on the first one.
In a long block, a paragraph, you don’t want balanced lines at all, because balancing a 15-line paragraph would be both expensive and pointless. What you want is for it not to end with one word alone on the final line.
Those are different objectives, which is why one property with one value couldn’t do both.
balance, for short things
h1, h2, h3,
.card__title,
figcaption,
blockquote {
text-wrap: balance;
}
The browser lays the block out, finds the narrowest width at which it still occupies the same number of lines, and uses that. So a heading that would break six-and-one becomes four-and-three, and the shape reads as deliberate.
It’s at its most obvious on centered text, where an uneven rag is much more visible, and on anything inside a card or a narrow column where the break points are constrained.
The important limitation: implementations cap this at a small number of lines, around six, because the layout work is repeated and it isn’t free. Past that cap it silently does nothing. Which is correct behavior and it means “why isn’t my paragraph balancing” has a boring answer.
pretty, for paragraphs
p, li { text-wrap: pretty; }
This one leaves the bulk of the paragraph alone and works on the end of it, preventing the single-word last line and improving the rag over the final few lines. Much cheaper than balancing, and appropriate for body copy at any length.
It’s in two engines and the third has it coming, and the degradation is that you get exactly what you have today, so there’s nothing to guard.
Typesetters have cared about this since the fifteenth century, and until about two years ago the web's only answer was a non-breaking space typed into the content by hand.
What we did before, all of which was worse
A non-breaking space between the last two words. Works, and it’s baked into the content, so it’s a decision about line breaking stored in the copy. It’s right at one width and wrong at others, it survives into the RSS feed and the search results snippet, and on a client site the marketing team editing that heading in a CMS will remove it without knowing what it was.
A <br> in a media query. Same problem, plus you now have breakpoint-specific markup, plus a <br> is read out by some assistive technology as a break in a sentence that has no break in it.
Asking the client to rewrite the heading. I have genuinely done this and I’m not proud of it. Changing what a business says about itself so that it fits the grid is the tail wagging the dog.
A JavaScript library. Several existed, they walked the text and inserted non-breaking spaces, they ran after layout so you could see the reflow, and they had to run again on resize.
All four are attempts to express a typographic preference through a mechanism that wasn’t designed to carry one. Both new values are the browser doing it during layout, where it’s cheap and where it can be right at every width.
The caveats
It’s a hint, not a guarantee. You can still get a break you don’t like, particularly with a very long word or a narrow column. It improves the distribution; it doesn’t promise a result.
Neither one fixes line length. A paragraph running to 140 characters a line is hard to read regardless of how the last line ends, and the fix for that is still a max-width in the region of 60 to 75 characters. These properties polish good typography; they don’t substitute for it.
Don’t put balance on everything. It’s tempting to write it on * and be done. It has a cost, it’s capped anyway on long blocks, and applying it globally means paying for the attempt everywhere it won’t be used.
Invisible when it’s right
This is a small thing. Nobody will look at a site and say the headings break nicely, and no client has ever asked for it.
It’s in the same category as the print stylesheet: invisible when it’s right, mildly embarrassing when it’s wrong, and the sort of detail that makes a page feel considered without anybody being able to say why. I like that category more than any other part of this job, which might be the most honest thing I’ve written this year 🥲