CSS text-wrap: balanced headlines, no lonely words
This note has an interactive demo. Try it in the original.
This Friday, 13 November 2026, text-wrap: balance becomes Baseline Widely available. Web.dev defines that status as 30 months after a feature works in every major engine, and webstatus.dev dates that moment to 13 May 2024, when Safari 17.5 shipped it.
It's a good excuse to look at the two text-wrap values that fix the typography problems we've all shipped: headlines with one long line and one tiny one, and paragraphs that end with a single word hanging on its own.
The problem: the browser only looks one line ahead
By default, the browser wraps text greedily. It fills a line until the next word doesn't fit, breaks, and moves on, without looking at what that does to the following lines. That's fast, and for most text it's fine.
But on a three-line headline, like the one in the demo, it often leaves "alone" by itself on the last line, and in a paragraph it creates a short last line that looks like a mistake.
Designers used to fix this by hand with <br>, between the last two words or a JavaScript library that measured the text. All of those break as soon as the width changes, the font loads late or someone edits the copy.
Two values, two jobs
text-wrap is a shorthand for text-wrap-mode (wrap or nowrap) and text-wrap-style. The interesting values are on the style side:
balancespreads the text so that every line has roughly the same number of characters. It's meant for short blocks: headlines, captions, pull quotes.prettykeeps normal wrapping but lets the browser use a slower algorithm that favours better layout over speed. In practice, it's what avoids the lonely last word in a paragraph.
This is all the CSS the demo above uses on its "After" panel:
h1, h2, h3, blockquote { text-wrap: balance; }
p { text-wrap: pretty; }
Play with the width slider (or press "Sweep") and watch the bars: each one is a real line, measured with a Range per word and getClientRects(). On the left, with plain wrap, at many widths the headline ends with a last line that is barely a fifth of the column, marked in orange as a "lonely word". On the right, balance evens the lines out, and the shortest line is usually two or three times longer.
You can switch each element between wrap, balance and pretty to see what each value does on its own.
What balance doesn't do
balance changes where lines break, not how wide the box is. The Chrome docs point out that a balanced heading can leave empty space on the right, which looks odd inside a card with a border or a background. If that bothers you, set a max-inline-size on the heading.
It also has a line limit, because balancing is expensive. According to MDN, it only kicks in for blocks of six lines or fewer in Chromium and ten or fewer in Firefox. Beyond that, the text wraps as usual.
That's why balance makes no sense on body copy: Chrome's own guide recommends using it selectively on headings and not on *.
What pretty does depends on the browser
The spec leaves the exact improvements to each browser, and the two engines that ship it really do different things:
- Chrome added
prettyin version 117. Its blog says it's focused on orphans: it avoids a single word on the last line, and adjusts earlier lines to make room. - Safari added it in version 26. The WebKit team says their implementation improves every line of the paragraph, not just the end: it evens out the ragged edge, avoids short lines and reduces hyphenation. The same post says Chromium only adjusts the last four lines.
On performance, MDN warns that pretty has a cost and recommends it for text where layout matters more than speed. The WebKit post is more relaxed: the number of elements doesn't matter, and you'd need paragraphs of hundreds or thousands of lines to notice anything.
Browser support and fallback
According to MDN's compatibility data, balance works in Chrome and Edge 114, Firefox 121 and Safari 17.5. pretty works in Chrome and Edge 117 and Safari 26, but not in Firefox yet.
The nice part is that you don't need a fallback. If a browser doesn't know the value, it ignores the declaration and the text keeps wrapping as always. Nothing breaks: it just looks slightly worse.
If you want to know which case you're in, as the demo does to show its warning, it's one line:
const hasPretty = CSS.supports('text-wrap', 'pretty');
In Firefox, the demo detects that pretty isn't supported, says so, and its paragraph behaves like wrap, which is exactly what your users will see.
One small gotcha: if you use the longhand, the property is text-wrap-style: balance, not text-wrap-mode, which only accepts wrap and nowrap.
What to keep an eye on
prettyin Firefox: until it arrives, a third of the picture is missing, although the fallback costs nothing.- Chromium and the rest of the paragraph: if Chromium extends
prettybeyond the last lines, as WebKit did, the result will look more alike across browsers. avoid-short-last-line: it already appears in thetext-wrap-stylegrammar on MDN, but MDN's compatibility data doesn't list it for any browser, so I'm not using it yet.
Sources
- text-wrap - CSS | MDN
- CSS text-wrap: balance | Chrome for Developers
- Better typography with text-wrap pretty | WebKit
- CSS text-wrap: pretty | Chrome for Developers
- Baseline | web.dev
Originally published at richi.codes.
Comments
No comments yet. Start the discussion.