10LOC
Chrome APIsintermediate

text-wrap: balance for balanced headlines with no JS

Published March 26, 2027

h1, h2, .card-title {
  text-wrap: balance;
  max-width: 40ch;
}

blockquote {
  text-wrap: balance;
  max-width: 60ch;
}

What

text-wrap: balance tells the browser to distribute a block's text evenly across its wrapped lines, instead of the default greedy algorithm that fills each line as full as possible before wrapping the leftover — the fix for a heading that wraps to one lonely word on its last line.

Why it matters

Default line-breaking is greedy: pack line one full, wrap whatever's left to line two, repeat. That frequently produces an ugly ratio — nine words on line one, one word alone on line two — and it's worse the shorter the text is, which is exactly the case for headlines, card titles, and pull quotes. The manual fixes have always been unsatisfying: hard-coded <br> tags that look right at one viewport width and wrong at every other, or a JS library that measures rendered text width and re-inserts breaks on every resize. text-wrap: balance computes the balanced distribution inside the layout engine itself, using the same line-breaking logic that's already running, and it re-balances automatically on every reflow — resize the window and it just stays balanced, for free.

How it works

The snippet applies text-wrap: balance to headings and blockquotes, paired with a max-width so there's actually more than one line to balance across:

  • The browser tries narrower maximum line-widths within the box and picks the narrowest one that still renders the content in the same number of lines the greedy algorithm would have used — it redistributes within the existing line count, it doesn't add an extra line to make things look neater.
  • max-width: 40ch isn't required by the property, but balancing has no effect on a heading that already fits on one line — give it room to wrap onto two or more lines somewhere in your breakpoints for the effect to be visible at all.

Gotchas

  • Chromium caps balancing at 6 lines, Firefox at 10 — past that, it silently falls back to normal greedy wrapping. Computing a balanced layout is expensive (roughly quadratic in line count), so this is a deliberate spec-permitted limit, not a bug. Reach for balance on headlines/captions/short blurbs by design, not on paragraph body text.
  • text-wrap is a shorthand. The actual logic lives on the longhand text-wrap-style (plus a separate text-wrap-mode for the wrap/no-wrap axis). text-wrap: balance is equivalent to text-wrap-style: balance and is what most code in the wild uses — the split just exists for cases where you need text-wrap-mode and text-wrap-style set independently, which the shorthand can't express.
  • No feature-detection needed. An unsupported browser just ignores the value and wraps text with the normal greedy algorithm — nothing breaks, it just isn't balanced.

Browser support: Baseline 2024 — Chrome 114+, Firefox 121+, Safari 17.5+. Safe to ship with zero fallback.

Related

  • text-wrap: pretty — a slower, more thorough algorithm aimed at avoiding orphans and improving overall typography, with narrower browser support than balance; a different heuristic for a similar goal.
  • hyphens: auto — operates on a different axis (breaking within long words), and composes with text-wrap: balance rather than replacing it.