When working with multilingual text, line breaks don’t always behave as expected. For example, we added text-wrap: balance
on WordPress.org, and quickly got community feedback that it led to awkward, unexpected breaks in Japanese and Korean. This got me thinking – how does text wrapping actually work across different languages, and what’s the best way to handle it?
What is text-wrap: balance
?
The text-wrap
property ensures text lines are of similar length when wrapping. It can prevent typographic widows/orphans, where a word is left alone on a new line. Here’s an example from the 6.7 release page:


There is also text-wrap: pretty
, which in my opinion is a nicer result, but it’s only supported in Chrome currently. Here’s that same example with “pretty.”

The browser determines line breaks when text wraps. In languages like English, it can use spaces & punctuation to determine where to put those breaks. However, some languages don’t use spaces – like Japanese, where the default behavior is to break at syllables. Those breaks could be mid-word. This is also true for Korean, despite Korean using spaces (perhaps a convention left over from before spaces were used).
While this is expected on full-width paragraph text, if you use text-wrap: balance
on headings, the break seems to happen randomly. Here’s an example from WordPress.org. First in Korean, then Japanese, then the equivalent in English.



There’s clearly still space for “WordPress” on the top line, so it’s odd for it to break. Obviously this is worse in English because we don’t have this convention, but still — it would be nicer if the word didn’t break, and the lines were still balanced.
word-break
Property
The word-break
property defines how the browser inserts line breaks. The default value (normal
) uses the customary rules for the given language as described above.
The break-all
value forces text to wrap without considering spaces, which mirrors the CJK style in English:

There’s keep-all
, which will prevent line breaks in CJK words (we’ll come back to this).
There’s also an experimental value called auto-phrase
. This tells the browser to use language-specific detection to automatically figure out what “phrases” should not be broken, and where line-breaks can happen. How this is done is up to the client (browser).
Balanced headings in Japanese
In 2023, Chrome 119 introduced word-break: auto-phrase
.1 It uses a machine learning tool (BudouX) to parse out words, which the browser then uses to wrap at those breaks, instead of splitting words. So we can use this combined with text-wrap: balance
to get the best of both– the lines will break around words, not in them, and it will try to keep heading lines equal length.
Here’s an example of this working — on the WordPress.org homepage, “One platform, a universe of possibilities.” In the first screenshot, nothing is applied so the first line of text is long, and the second much shorter. In the second, the text is literally balanced, but the word “platform” is cut off to make it so. In the last example, “platform” is all on the first line, and the line break occurs after the comma.



And here’s the CSS I’ve ended up with. By default, it should use text-wrap: balance
, so that English headings get the style. On Japanese, it should reset back to initial (wrap), to prevent the unexpected line breaks. Lastly, if the browser supports auto-phrase (using @supports to check), the text is set back to balance and auto-phrase is enabled.
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
&:lang(ja) {
text-wrap: initial;
@supports (word-break: auto-phrase) {
word-break: auto-phrase;
text-wrap: balance;
}
}
}
I’m not sure if it would be appropriate to set word-break: auto-phrase;
on the whole document, regardless of balance
d wrapping— if that would even be desired by native Japanese readers. I also haven’t seen anything about the performance impacts, but would be curious to test it out more (some basic tests show it’s 3x slower than keep-all
& break-all
when used on a full page of text).
Phrase detection and other languages
The BudouX library also handles word detection in Thai, Simplified Chinese, and Traditional Chinese. These are not supported in Chrome yet, as far as I can tell. The Chrome blog post initially says this will come to Korean too, but BudouX itself says it’s probably not necessary.
Balanced headings in Korean
So if we don’t have or need auto-phrase
, how can we have balanced headings without breaking words? Well, I said we’d come back to keep-all
🙂
This prevents wrapping in the middle of words, only wrapping at spaces and punctuation. This can be an issue on small screens, because if a word is long or in a large font, preventing wrapping will over flow the container. Here’s that first example again, from the WordPress.org homepage: “Meet the WordPress community.”



Again, here’s the CSS I used. Same as before, set up balance
for other languages, and for Korean, add word-break: keep-all
. The last step here is to add a rule for small screen sizes that will reset back to allow wrapping. In our case, 480px seems to be the right size for most headings, otherwise long words will break out of the container and scroll.
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
&:lang(ko) {
word-break: keep-all;
@media (max-width: 480px) {
text-wrap: wrap;
word-break: initial;
}
}
}
h/t to @taggon for the original suggestion.
Getting text wrapping right across languages isn’t always straightforward. While text-wrap: balance
works well for English, it can cause awkward breaks in Japanese and Korean.
If you’re going to use text-wrap: balance
2, here’s what works best:
- For English,
text-wrap: balance
works well for maintaining even line lengths. - For Japanese, using
word-break: auto-phrase
(where supported) ensures that words don’t break unnaturally while still balancing lines. - For Korean,
word-break: keep-all
prevents mid-word breaks but may require media queries to handle small screens.
As browser support improves, text wrapping should get smarter, but for now, testing different approaches is key. Have you noticed similar issues in other languages? Let me know on mastodon or bluesky!
-
auto-phrase
is also supported in Safari with a feature flag, using theirCFStringTokenizer
(not BudouX). As far as I can tell, it hasn’t been picked up by Firefox yet. ↩︎ - and maybe you shouldn’t— even with these changes I’ve gotten mixed feedback on a PR to implement this on WordPress.org. ↩︎