You’re on your phone, reading a card component or a hero headline, and the last word of the paragraph just… sits there. Alone. On its own line. One word. Maybe two letters. It looks broken, even though the code is technically fine. You’ve probably pushed a <br> tag in there manually just to fix it, or jammed in a between the last two words and hoped for the best. It works, until the viewport changes, the font size shifts, or a content editor updates the copy. Then it breaks again.
I’m Rohan Ratnayake, and I’ve spent the last 5 years as a front-end UI engineer specializing in typography systems and design-to-code translation. I’ve watched this exact problem get “fixed” with brittle hacks in codebases that later blew up during a responsive redesign. I once inherited a marketing site where the previous dev had 47 manual <br> tags scattered across heading elements — every single one inserted just to prevent dangling words. When the client updated their tagline from 6 words to 8, the whole page looked like it was formatted by someone having a bad day.
The real issue isn’t your eye for detail. It’s that CSS didn’t have a clean, native answer for this until recently. Now it does. text-wrap: balance and text-wrap: pretty are both shipping in modern browsers, and they solve the orphan problem without a single line of JavaScript. Here’s how to use them correctly, where each one actually helps, and where they’ll disappoint you if you’re not careful.
What “Orphan Words” Actually Are (And Why They Look So Bad)

In typography, an orphan is a single short word sitting alone at the end of a paragraph or heading. On desktop, it’s annoying. On mobile, where line widths are narrow, it happens constantly because the browser’s default line-wrapping algorithm only cares about fitting words within the container — it has no concept of visual balance.
The browser wraps text greedily. It fills each line as much as possible, then drops to the next. That approach is fast and predictable, but it produces uneven line endings that hurt readability and look unprofessional in display-sized text.
Here’s a quick comparison of what you’re dealing with:
| Wrapping Method | What It Does | Visual Result |
|---|---|---|
| Default (greedy) | Fills each line, wraps at word boundary | Uneven line lengths, frequent orphans |
text-wrap: balance | Distributes text so lines are roughly equal width | Clean, centered-looking blocks |
text-wrap: pretty | Optimizes last few lines to avoid orphans | Paragraphs with natural flow, no lone words |
text-wrap: balance — What It Actually Does
text-wrap: balance tells the browser to redistribute text so that all lines in the element are as close to the same length as possible. It’s not just about the last line — it recalculates the whole block.
This is genuinely useful for:
- Headings (H1, H2, H3)
- Card subtitles
- Pull quotes
- CTA button labels that wrap
- Hero section copy
The CSS is simple:
h1, h2, h3 {
text-wrap: balance;
}
That’s the whole fix for headings. No wrapper div. No JavaScript resize observer. No manual line breaks.
Here’s the thing though — text-wrap: balance comes with a hard browser-side limit: it only works on elements with six lines or fewer. Chrome’s implementation, which set the baseline for this feature, caps it there because the balancing algorithm is computationally expensive. On a paragraph with 10 lines, it silently falls back to the default behavior.
So if you apply text-wrap: balance to body copy and wonder why nothing changed, that’s why.
Browser support as of mid-2025:
| Browser | Supports text-wrap: balance |
|---|---|
| Chrome 114+ | ✅ Yes |
| Edge 114+ | ✅ Yes |
| Firefox 121+ | ✅ Yes |
| Safari 17.4+ | ✅ Yes |
| Opera 100+ | ✅ Yes |
You can verify the current support data directly on MDN Web Docs, which stays current with browser rollouts.
text-wrap: pretty — The Better Choice for Paragraphs
If balance is for headings, pretty is for paragraphs. It’s a softer optimization that specifically targets the last 4 lines of a text block. The browser looks at those lines and adjusts wrapping to avoid leaving a single short word stranded at the end.
p {
text-wrap: pretty;
}
The difference matters. balance tries to equalize every line. pretty only touches the tail end of the block, which makes it much cheaper to compute — and safe to apply to long-form body copy without any performance cost.
A practical way to think about it:
- Use
balancewhen the whole element is a heading or display text — you want the entire visual weight to feel centered and controlled. - Use
prettywhen you’re dealing with paragraphs — you just want the last line to look intentional, not truncated.
Side-by-side behavior:
| Property | Line Scope | Best For | Performance Cost |
|---|---|---|---|
text-wrap: balance | All lines (max 6) | Headings, short blocks | Medium |
text-wrap: pretty | Last ~4 lines | Body paragraphs | Low |
Default (wrap) | All lines | N/A | Minimal |
The Right Way to Set This Up Globally

Most people add this one property at a time and forget about it. The smarter move is to set it once in your base styles and let it do the work everywhere.
/* Base typography reset */
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}
p, li, blockquote {
text-wrap: pretty;
}
This pattern handles 90% of orphan issues site-wide without touching a single piece of content. The remaining 10% is usually edge cases in custom components where a heading wraps to 7+ lines — which honestly means the heading is too long and should be shortened anyway.
One thing I’d add: if you’re working with a design system that uses utility classes, add these as single-purpose utilities too.
.text-balance { text-wrap: balance; }
.text-pretty { text-wrap: pretty; }
That way, when a designer needs to override the default on a specific card or pull quote, they have a clean handle instead of writing inline styles.
Where People Get This Wrong
The most common mistake I see is applying text-wrap: balance to everything — body text, long descriptions, feature lists — and then being confused when it has no effect. Because of the six-line limit, it silently does nothing on long text. You won’t get an error. You won’t get a console warning. It just falls back quietly.
The second mistake is thinking this replaces max-width on text containers. It doesn’t. If your paragraph stretches across a 1400px screen with no max-width, the lines are too long for comfortable reading regardless of how they wrap at the end. These properties fix how the last lines look, not how readable the overall block is. You still need max-width or max-inline-size on your text containers — around 65ch to 75ch is a solid range for body text according to long-standing readability guidance from W3C’s accessibility documentation.
A third mistake: testing only on desktop. Orphan words show up on narrow viewports — 320px to 430px is where this problem is most visible. If you add text-wrap: balance to a heading and check it on your 1440px monitor, you might not even see a difference. Always test at mobile widths.
Quick checklist before you ship:
- [ ] Are you applying
balanceto elements with 6 lines or fewer? - [ ] Is
prettyset on paragraphs and list items? - [ ] Have you tested at 375px viewport width?
- [ ] Do your text containers have a
max-widthset? - [ ] Are you avoiding
<br>tags for layout purposes?
What About the text-wrap: stable Value?
There’s a third value worth knowing: text-wrap: stable. It doesn’t fix orphans — its job is different. It prevents text from reflowing when a user edits a contenteditable element. The characters before the cursor don’t jump around while you type. This is relevant for rich text editors, not for static content. Don’t use stable thinking it’s a more powerful version of pretty. It isn’t.
A Real Example: Before and After

Here’s a heading that was breaking on mobile without intervention:
Before (default wrapping):
The fastest way to ship
clean, readable
UI
That lone “UI” at the end on a narrow screen looked like a layout bug to every user who saw it. With one line of CSS:
h2 {
text-wrap: balance;
}
After:
The fastest way to ship
clean, readable UI
Two lines. Balanced. Done. No media query. No JavaScript. No <br>.
FAQs
Does text-wrap: balance hurt performance? For headings and short blocks, no. The six-line limit exists precisely to keep the computation fast. Applying it broadly to long paragraphs won’t hurt either — it just won’t do anything because it silently falls back on blocks exceeding the line limit.
Can I use these properties in a CSS framework like Tailwind? Tailwind v3.3+ added text-balance and text-pretty as utility classes. If you’re on an older version, add them manually in your config or as custom CSS.
What if I need to support older browsers that don’t have this? The property degrades gracefully — unsupported browsers just get the default wrapping behavior. No broken layouts, no errors. You don’t need a fallback, but if you’re building for a user base with significant legacy browser traffic, test to confirm the visual difference is acceptable without the fix.
Wrapping Up
The gap between “this looks rough on mobile” and “this looks deliberate” is often a single CSS property. text-wrap: balance handles headings. text-wrap: pretty handles paragraphs. Set them both in your base styles today, and you can stop manually patching line breaks every time a content update shifts your text.
Stop reaching for <br> tags or JavaScript resize hacks. Open your global stylesheet, add those two rules, and test at 375px. That’s the whole job.

