coldwa.st
All guidesProgrammingWebDataToolsDatabasesHaskellConceptsCabal & buildsToolchainCompilerPerformanceEditor & HLS

CSS · typography · web standards

CSS text-wrap: balance

By ColdwastUpdated Sep 5, 20266 min read#css
The word Bold set in a large serif face on a dictionary page, sharp in the centre of the frame while the surrounding definition text about typography blurs away at both edges
A dictionary entry photographed close up: the headword sits alone on its line while the definition around it runs full width. The problem this property solves is the opposite case, a heading whose last line carries a single word.

You have written a three-line heading and the third line contains one word. It looks like a mistake, it is not one, and until recently the only fixes were a manual line break or a non-breaking space that stopped being correct the moment the viewport changed.

The declaration is one line:

h1, h2, h3 {
  text-wrap: balance;
}

The browser then distributes the words so that the lines come out close to equal length, instead of filling each line greedily and leaving whatever remains on the last one.

Why it is restricted to short text, and this is in the specification

Normal line breaking is greedy: fill a line, move on, never look back. Balancing is not, because the browser has to try several distributions and compare them. That work grows with the number of lines, which is why the specification allows implementations to cap it.

Chromium applies the property to blocks of six lines or fewer and ignores it beyond that. The limit is not a bug and not something to work around: it is what keeps a paragraph of forty lines from costing a layout pass it does not deserve. Applying balance to p is therefore not dangerous, it is simply ignored on anything long, which makes it a declaration that does nothing while looking like it does something.

An open book of poetry lying on a dark wooden table, the right hand page in focus showing a poem titled To a friend who sent me some roses under the header Poems published in 1817, the left page falling out of focus
A printed poem, where every line break was decided by a person. On the web the browser decides, and until this property existed it decided greedily with no way to ask for anything else.

balance, pretty, and stable are three different jobs

text-wrap: pretty is the one for body copy. It does not equalise line lengths; it avoids typographic orphans, the single word left on the final line of a paragraph, and it does so with a much smaller performance cost because it only reconsiders the end of the block.

text-wrap: stable matters for text that changes as the user types. Without it, editing the middle of a paragraph can reflow every following line and make the text appear to jump. With it, lines before the cursor stay where they are.

The short version: balance for headings, pretty for paragraphs, stable for editable regions. They are not stronger and weaker versions of one another.

What happens in a browser that does not support it

Nothing, which is the correct behaviour. An unknown value for text-wrap is dropped at parse time and the element wraps the way it always did. That makes this a genuine progressive enhancement: no feature query required, no fallback to write, no layout difference beyond a heading that breaks less elegantly.

If you want to branch on it anyway, @supports (text-wrap: balance) works, but for this property it is usually ceremony around a no-op.

Where it goes wrong in practice

Applied to every element. A universal selector with balance is silently ignored on long blocks and costs layout work on the short ones. Target headings, captions, and blockquotes, which is where the visual problem actually appears.

Combined with a fixed width. Balancing chooses break points; it does not change how much room the text has. A heading in a narrow column with long words will still break awkwardly, and the property has nothing to offer there.

Expected to fix a widow in body text. That is the job of pretty. Reaching for balance and concluding the feature is broken is the most common way people meet the six line cap without realising it.

The one line worth adopting

For most sites this is a three line change in a stylesheet, applied to headings only:

h1, h2, h3, h4, figcaption, blockquote {
  text-wrap: balance;
}

p, li {
  text-wrap: pretty;
}

It degrades to nothing where unsupported, it needs no JavaScript, and it removes a category of manual line breaks from your templates. Related reading: CSS container queries.