
text-wrap: balance and pretty: Better Typography with One Line of CSS
Every designer has seen it: a big, beautiful headline that breaks into one long line followed by a single sad word on the second line. Or a paragraph that ends with one short word stranded on its own, an orphan that makes the whole block look unfinished. For years the fixes were hacks: manual <br> tags, non-breaking spaces between the last two words, or a JavaScript library that measured text and rewrote your markup.
Modern CSS has a much better answer. The text-wrap property now accepts two values, balance and pretty, that ask the browser to make smarter line-breaking decisions. Each is one line of CSS, both are safe to ship today, and both degrade gracefully in browsers that don't understand them.
In this guide, I'll explain how the browser normally wraps text, what each value changes, where to use them, and the handful of gotchas you should know before you add them to your base stylesheet.
How Browsers Wrap Text by Default
To understand why balance and pretty exist, you need to know how line breaking normally works. Browsers use a greedy algorithm: they place as many words as possible on the first line, then move to the next line and repeat. Each line is decided on its own, without looking ahead.
Greedy wrapping is fast, and for long paragraphs it's usually fine. But it has two predictable failure modes:
- Uneven short blocks : A headline that is slightly wider than its container fills the first line completely and pushes one or two words onto the second line.
- Orphans at the end of paragraphs : Because the algorithm never looks back, the final line can end up holding a single short word.
Typesetting systems like TeX solved this decades ago by considering the whole paragraph at once and picking the breaks that produce the most even result. That's more expensive to calculate, which is why browsers didn't do it by default. The new text-wrap values let you opt into smarter behavior exactly where it matters.
The text-wrap Property at a Glance
text-wrap is now a shorthand for two longhand properties:
text-wrap-modecontrols whether text wraps:wrapornowrap.text-wrap-stylecontrols how it wraps:auto,balance,pretty, orstable.
/* These two declarations are equivalent */
h1 {
text-wrap: balance;
}
h1 {
text-wrap-mode: wrap;
text-wrap-style: balance;
}
You'll almost always use the shorthand. The values you'll reach for are:
| Value | What it does | Best for |
|---|---|---|
wrap | Normal greedy wrapping (the default) | Everything, by default |
nowrap | Prevents wrapping, like white-space: nowrap | Labels, badges, table headers |
balance | Evens out line lengths across the whole block | Headings, captions, pull quotes |
pretty | Uses a slower algorithm that avoids orphans and ragged endings | Body paragraphs |
stable | Keeps earlier lines fixed while the user edits later ones | Editable content |
Related change worth knowing: white-space is now itself a shorthand for white-space-collapse and text-wrap-mode. You don't need to rewrite existing white-space rules, they keep working exactly as before.
text-wrap: balance
balance tells the browser to distribute text so that every line is roughly the same length. Instead of a full first line and a stubby second line, you get two lines of similar width.
A Basic Example
<article class="post">
<h1 class="post__title">
Designing Accessible Color Systems for Modern Product Teams
</h1>
<p class="post__lede">
A practical approach to building palettes that pass contrast checks without
looking dull.
</p>
</article>
.post__title {
max-width: 20ch;
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.1;
text-wrap: balance;
}
Without balance, a narrow screen might render the title as "Designing Accessible Color Systems for Modern Product" and then "Teams" alone on the last line. With balance, the browser searches for the break points that give each line a similar width, so you get something closer to three even lines.
Why It's Limited to Short Blocks
Balancing requires the browser to try multiple candidate widths and compare the results, which gets expensive as the number of lines grows. To keep this cheap, browsers cap it. Chromium balances blocks of up to six lines, and Firefox allows a few more. If an element exceeds the limit, the browser simply falls back to normal wrapping for it.
That cap is a design hint as much as a technical detail: balance is meant for short text. Use it on:
h1throughh6- Card titles and hero taglines
figcaptionandblockquoteelements- Short intro or lede paragraphs
- Button labels and navigation items that may wrap on small screens
Don't bother putting it on long article paragraphs. It won't do anything there, and even-length lines in body text actually look strange.
A Sensible Global Default
Many teams now add this to their base styles:
h1,
h2,
h3,
h4,
h5,
h6,
blockquote,
figcaption,
caption {
text-wrap: balance;
}
It's one of the few "set and forget" rules in CSS. Headings that fit on one line are unaffected, and headings that wrap look intentional instead of accidental.
Balance and the Box Width
Here's the subtle part. balance changes where the lines break, but it doesn't shrink the element's box. If your heading is 600px wide and balancing makes each line about 420px, the box stays 600px, with empty space on the right.
For left-aligned text on a plain background, you'll never notice. It becomes visible when the element has a background, a border, or is centered with a fixed width:
/* The pill background still spans the full width */
.badge-heading {
display: inline-block;
max-width: 24rem;
padding: 0.5rem 1rem;
background: #1e293b;
border-radius: 999px;
text-wrap: balance;
}
If that trailing space bothers you, center the text with text-align: center so the space distributes evenly, or rethink whether a box around multi-line text is the right design. There isn't currently a pure CSS way to shrink-wrap the box to the balanced line width.
text-wrap: pretty
pretty targets the other classic problem: the ragged or orphaned ending of a paragraph. It tells the browser it may use a slower, smarter algorithm that considers more than one line at a time, favoring better typography over raw speed.
.article-body p {
text-wrap: pretty;
}
The spec deliberately doesn't prescribe an exact algorithm, so browsers differ in how much they do:
- Chromium focuses on the end of the paragraph. It looks at the last few lines and adjusts breaks so the final line isn't a single short word.
- Safari takes a broader approach, evaluating more of the paragraph to reduce raggedness, avoid bad hyphenation, and prevent short last lines.
Both approaches solve the most visible problem, orphans, and both are harmless if the paragraph is already fine.
Before and After
Consider a narrow card with a description:
<div class="card">
<h3 class="card__title">Offline-first sync for field teams</h3>
<p class="card__text">
Capture inspections, photos, and signatures without a signal, then sync
everything automatically when you reconnect.
</p>
</div>
.card {
width: min(100%, 22rem);
padding: 1.5rem;
border-radius: 12px;
background: #0f172a;
color: #e2e8f0;
}
.card__title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
text-wrap: balance;
}
.card__text {
margin: 0;
line-height: 1.6;
text-wrap: pretty;
}
With greedy wrapping, "reconnect." may end up alone on the last line. With pretty, the browser pulls a word or two down from the previous line so the final line has some company.
Why Not Use pretty Everywhere?
Because it costs more to compute, pretty isn't meant for every piece of text on the page. In practice, the cost is small for normal article content, but it's wise to scope it:
- Apply it to body text in content areas, like articles, cards, and marketing copy.
- Skip it on huge, frequently re-rendered text, like live chat logs or giant data tables.
- Skip it on editable text, where
stableis a better fit (more below).
/* Good: scoped to reading content */
.prose p,
.prose li {
text-wrap: pretty;
}
balance vs. pretty: Choosing the Right One
It's easy to mix these up, so here's the rule of thumb:
balancemakes every line similar in length. Great for short text where symmetry matters.prettyleaves most lines as they are and fixes the ending. Great for paragraphs where you want a natural ragged edge but no orphan.
Using balance on body copy produces blocks that look oddly narrow. Using pretty on a headline usually helps a bit, but balance gives the cleaner result. A typical stylesheet uses both:
:is(h1, h2, h3, h4, h5, h6, blockquote, figcaption) {
text-wrap: balance;
}
:is(p, li, dd) {
text-wrap: pretty;
}
text-wrap: stable for Editable Content
The third style value, stable, solves a problem you might not have noticed. When a user types into an editable area, a smart wrapping algorithm could re-flow earlier lines as they add words, making text jump around under their cursor. stable guarantees that lines before the one being edited stay put.
textarea,
[contenteditable="true"] {
text-wrap: stable;
}
It behaves much like the default greedy wrapping, so you won't see a visual difference. It's a guarantee, not an effect. Use it if you've set pretty globally and want to make sure editors stay calm.
text-wrap: nowrap as a Replacement for white-space
Since text-wrap-mode now exists, text-wrap: nowrap is the modern way to keep text on one line:
.tag {
text-wrap: nowrap;
}
This does the same thing as white-space: nowrap for wrapping purposes, but it doesn't touch whitespace collapsing. If you need the widest browser support today, white-space: nowrap remains the safer choice, and there's no harm in keeping it.
Common Pitfalls and Best Practices
Don't Rely on It for Content Meaning
Line breaks created by balance or pretty are purely visual. If a line break is essential to meaning, like in a poem or an address, use real markup such as <br> or separate elements.
Remember the Line Limit
If a heading is so long that it wraps to eight or nine lines on mobile, balance will quietly do nothing in Chromium. That's usually a sign the heading is too long or the font size too large for that breakpoint. Use clamp() to scale headings down on narrow screens:
h1 {
font-size: clamp(1.75rem, 1.2rem + 3vw, 3.5rem);
text-wrap: balance;
}
Watch Out for Width Constraints
balance works within the available width. If you set max-width in ch units, you control the upper bound and balance fills in the rest. A combination like this is very reliable:
.hero__title {
max-width: 18ch;
margin-inline: auto;
text-align: center;
text-wrap: balance;
}
Remove Old Hacks Carefully
If your CMS content includes between the final two words, those hacks will still work alongside pretty, but they can fight with balance by gluing words together in ways that limit the options. Once you've added text-wrap, consider removing manual non-breaking spaces from headings.
Similarly, if you use a JavaScript balancing library, you can often delete it entirely. Keep it only if you need balanced text in older browsers that matter to your audience.
Pair pretty with Hyphenation Thoughtfully
hyphens: auto and text-wrap: pretty work well together for narrow columns, since hyphenation gives the algorithm more break opportunities. Make sure the element has a correct lang attribute, because hyphenation dictionaries depend on it:
<article class="prose" lang="en">
<p>
Internationalization and accessibility requirements shape every decision.
</p>
</article>
.prose p {
hyphens: auto;
text-wrap: pretty;
}
Test at Real Widths
The only way to see the effect is to resize the viewport. Open DevTools, turn on responsive mode, and drag the width slowly. Toggle the text-wrap declaration on and off in the Styles panel to compare. It's one of the most satisfying before-and-after checks in CSS.
Browser Support
Both values are true progressive enhancements. A browser that doesn't recognize them ignores the declaration and wraps text normally, so there's no layout risk and no need for a fallback.
text-wrap: balanceis supported in all major evergreen browsers: Chrome, Edge, Firefox, and Safari.text-wrap: prettyis supported in Chromium-based browsers and in recent Safari versions. Support in Firefox has lagged behind, so check caniuse for its current status.text-wrap: stableand thetext-wrap-mode/text-wrap-stylelonghands have broad support in current browsers, but older versions only understood thetext-wrapshorthand.
If you want to apply a style only when balancing is available, for example to tweak a heading's max-width that you'd otherwise set tighter, @supports works:
.section-title {
max-width: 16ch;
}
@supports (text-wrap: balance) {
.section-title {
max-width: 24ch;
text-wrap: balance;
}
}
In most cases, though, you don't need @supports at all. Just add the declaration and let each browser do its best.
Conclusion
text-wrap: balance and text-wrap: pretty are rare CSS features: tiny to adopt, impossible to break a layout with, and immediately visible to anyone who cares about typography. Put balance on headings, captions, and other short text so that multi-line titles look deliberate. Put pretty on paragraphs in your content areas so they stop ending with lonely words. Reach for stable if you have editable text and a global pretty rule.
Add those few lines to your base stylesheet today, delete the non-breaking space hacks and balancing scripts you no longer need, and let the browser handle what typesetters have handled by hand for centuries.


