Text Wrap: balance and pretty
The Problem
Headings and short text blocks often end up with awkward last lines — a single orphaned word dangling on its own line, creating an uneven visual shape. Developers traditionally worked around this with manual characters, explicit <br> tags, or JavaScript-based solutions that measure text width and reflow content. These approaches are brittle: they break at different viewport sizes, different font sizes, and in different languages. Now CSS handles this natively with the text-wrap property.
The Solution
The text-wrap property offers three values beyond the default wrap that give fine-grained control over how text breaks across lines:
balance— Distributes text evenly across all lines so no single line is significantly longer or shorter than others. Ideal for headings, labels, and short text blocks.pretty— Prevents orphaned words on the last line of a paragraph. The browser adjusts earlier line breaks to ensure the final line has at least two words. Designed for body text.stable— Ensures lines that have already been laid out don't re-wrap when editable content changes. Useful forcontenteditableareas and live editing interfaces.
Core Principles
Use balance for Headings and Short Text
text-wrap: balance works by making all lines roughly equal in width. The browser calculates the optimal line breaks so that the shortest line is as long as possible. This creates a visually centered, even block of text — perfect for headings, pull quotes, and captions.
However, browsers limit balancing to approximately 6 lines for performance reasons. Beyond that threshold, the text falls back to normal wrapping.
Use pretty for Body Text
text-wrap: pretty focuses specifically on the last line. It prevents a single orphaned word from appearing alone on the final line by adjusting earlier line breaks. Unlike balance, it doesn't try to equalize all lines — it only ensures the ending looks clean.
Use stable for Editable Content
text-wrap: stable prevents previously laid-out lines from shifting when new content is typed after them. This avoids the jarring re-wrap effect in contenteditable elements and text editors.
Code Examples
Basic Usage
h1,
h2,
h3 {
text-wrap: balance;
}
p {
text-wrap: pretty;
}
Production Typography System
/* Balance all heading levels */
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
}
/* Prevent orphans in body text */
p,
li,
blockquote {
text-wrap: pretty;
}
/* Stable wrapping for editable areas */
[contenteditable] {
text-wrap: stable;
}
CJK and Japanese Text Considerations
text-wrap: balance and text-wrap: pretty were designed with alphabetic languages in mind and behave unexpectedly with Japanese and other CJK (Chinese, Japanese, Korean) text. If you apply these properties globally to headings or paragraphs, Japanese text will often render with awkward line breaks and unusually large trailing whitespace on the right side.
Why It Breaks
In alphabetic languages, the browser uses spaces and punctuation to identify word boundaries when redistributing line breaks. Japanese has no spaces between words — every character position is a valid break point. When text-wrap: balance tries to equalize line lengths, it splits text at arbitrary character positions, producing breaks that cut through the middle of natural phrases. The result is a heading where the first line ends mid-phrase, leaving a conspicuously large gap on the right.
The Japanese balanced version often breaks at an unnatural position — for example splitting after a particle like 「の」 or 「な」 — because the browser has no awareness of Japanese word boundaries. The line break feels arbitrary, and the right margin of the first line becomes conspicuously large.
The word-break: auto-phrase Workaround
Chrome 119+ introduced word-break: auto-phrase, which uses the BudouX machine learning engine to identify natural phrase boundaries (文節) in Japanese text. When combined with text-wrap: balance, the browser balances around those phrase boundaries instead of arbitrary character positions.
h1,
h2,
h3 {
text-wrap: balance;
word-break: auto-phrase; /* Chrome 119+ — improves Japanese line breaks */
}
Note that word-break: auto-phrase requires lang="ja" on the element or an ancestor to activate Japanese phrase detection. Without the lang attribute, the property has no effect. Firefox does not yet support auto-phrase. Safari support is limited — check current browser compatibility before relying on it.
Locale-Scoped Fallback Pattern
For Japanese or multilingual sites, the safest approach is to reset text-wrap for Japanese content via the lang attribute selector:
/* Apply balance globally */
h1,
h2,
h3 {
text-wrap: balance;
word-break: auto-phrase; /* helps in Chrome */
}
/* Reset for Japanese where balance misbehaves without auto-phrase support */
[lang="ja"] :is(h1, h2, h3) {
text-wrap: initial;
}
/* Re-enable balance in Chrome which supports auto-phrase */
@supports (word-break: auto-phrase) {
[lang="ja"] :is(h1, h2, h3) {
text-wrap: balance;
}
}
Summary
| Property | English | Japanese |
|---|---|---|
text-wrap: balance | Works well | Breaks at arbitrary character positions |
text-wrap: pretty | Works well | Largely harmless but orphan concept doesn't map well to CJK |
word-break: auto-phrase | No effect | Improves phrase-aware breaking (Chrome 119+ only) |
Recommendation: For Japanese or multilingual projects, avoid applying text-wrap: balance to headings unconditionally. Either skip it for Japanese content, use the locale-scoped @supports pattern above, or accept that the improvement will only appear in Chrome.
Browser Support
text-wrap: balance— Supported in Chrome 114+, Edge 114+, Firefox 121+, and Safari 17.5+. Widely available as of 2025.text-wrap: pretty— Supported in Chrome 117+, Edge 117+, and Safari 17.4+. Firefox support is still pending as of early 2026.text-wrap: stable— Supported in Chrome 120+, Edge 120+, and Firefox 121+. Safari support is pending.
All three values degrade gracefully — unsupported browsers simply use the default text-wrap: wrap behavior, so there is no risk in applying them today.
Common AI Mistakes
- Applying
text-wrap: balanceto long body text paragraphs — it only works on blocks of approximately 6 lines or fewer, and even when it does apply, it creates an unnaturally narrow text block - Using JavaScript to calculate line breaks and insert
<br>tags whentext-wrap: balancesolves the same problem natively - Inserting
between the last two words to prevent orphans — this is fragile and breaks at different viewport widths. Usetext-wrap: prettyinstead - Confusing
text-wrap: balancewithtext-align: center— balance adjusts line break positions to equalize line lengths, it does not change alignment - Not setting
text-wrap: balanceon heading elements by default — this should be a baseline style for all heading levels - Applying
text-wrap: balanceto elements that need a specific width, like navigation items or badges — balance can change the intrinsic width of the element unexpectedly
When to Use
text-wrap: balance
- Headings (
h1throughh6) - Pull quotes and blockquote text
- Captions and figcaptions
- Card titles and hero text
- Any short text block (6 lines or fewer) where visual symmetry matters
text-wrap: pretty
- Body paragraphs
- List items with wrapping text
- Descriptions and summaries
- Any long-form text where you want to avoid a single orphaned word on the last line
text-wrap: stable
contenteditableelements- Live text editors and input areas
- Chat message composition fields
- Any context where text is being actively edited and re-wrapping would be disruptive