How to avoid orphan word on a line with css?
Asked Answered
A

4

12

What I mean with orphans word is a single word on a line. Possible with help of CSS to avoid any line with only one word?

For example SERUM is an orphand word:

ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT BODY
SERUM

It should look like this instead

ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT
BODY SERUM

enter image description here

Ashling answered 13/12, 2021 at 17:43 Comment(8)
CSS isn't a scripting language, so you can't implement that kind of logic. The best you could do would be to wrap two or more words in an element and not let it wrap.Fed
So what you're actually referring to is called a runt in print, not an orphan which deals with page breaks. Your only real options are to limit the width of the container or add horizontal padding using magic numbers that will force a second word to join your runt at the widest screen size and make sure that the container for an individual item never goes beyond that width. It's simple with CSS Grid, if you share some code someone can recommend how you could manipulate it to fit what you're looking for.Wifely
But I gave up on this kind of pixel-perfection years ago. The modern web flexes and flows. Hit the low-hanging fruit and embrace it as it is.Fed
Add   between the last two wordsDaze
An idea is to add padding to the left and the right side of the text container to be sure that you have less text on the first line. Apart of that you cannot make it only with CSS.Irksome
With only CSS it is not possible, for this case the best solution will be to reduce the text container width, try out a 80% of the actual width for the expected result.Venegas
Oh, man... orphan-words: avoid; would be so much more useful than hyphens: auto; to me at least. And seems like it would be way easier to implement. Sadly it does not exist at the moment.Izaak
text-wrap: balanceAnabranch
R
14

A quick and easy way to prevent two words from breaking is to insert a non breaking space   between the words

ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT BODY SERUM

It won't be visible on the front end but those words will stay together even if there enough space for just one of them.

<h2>ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT BODY&nbsp;SERUM</h2>
Rennet answered 13/12, 2021 at 18:0 Comment(4)
Junky,Thank you for your reply, the issue with this solution is that when I send the order to an external applicaiton that for example print out this text on a piece of papper (then &nbsp; will appears on the paper, right?)Ashling
No it shouldn't print. I updated my answer with a code snippet for you to test.Rennet
Amazing Junky! Thank you!Ashling
This is definitely very useful in some situations. But the problems with this and the other answer are the same: If you automated this, it could break mobile layouts if the last two words are very long. If you don't automate it, it's difficult to train non-technical editors how to implement it correctly. We've done some funky server level stuff, like look at the string length and if it's longer than N characters long, find the space character closest to the middle and replace it with a <br>. But that approach has drawbacks too.Izaak
E
9

This does not have the best browser support yet, but you can use text-wrap: pretty; to avoid orphans.

CSS

p {
  text-wrap: pretty;
}

HTML

<p>ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT
BODY SERUM</p>
Erickericka answered 24/11, 2023 at 13:12 Comment(3)
This is great. More browser support would be ideal but at least the majority will still see pretty text wrapping with Chrome and Edge support.Brott
text-wrap: balance is much more widely supported: caniuse.com/mdn-css_properties_text-wrap_balanceAnabranch
Personally, I went with text-wrap: balance; text-wrap: pretty. IMO 'pretty' gives a slightly nicer render, grouping the last 2 or so words together. Chromium browsers will run with that given the option. Firefox ignores 'pretty' for now, but is fine with 'balance', which is certainly an improvement over nothing.Britska
B
2

You can do it with white-space: nowrap

span {
  white-space: nowrap;
}
<h2>ECSTASY OF EXISTENCE FIRMING ANTIOXIDANT <span>BODY SERUM</span></h2>
Borowski answered 13/12, 2021 at 18:51 Comment(0)
T
0

If you're fine using JavaScript, the Adopt-Words library seems to do the job.

https://www.cssscript.com/prevent-orphans-adopt-words/

It's under 2KB in size, though I haven't tested the runtime performance for large pages with a lot of text. By default it doesn't apply itself to the whole page: you specify what you want it to run on. So

AdoptWords.init('.example *', {
  // options here
})

would only prevent orphan words on elements with the class example. You could probably apply it to all p, span or whatever tags, but might incur a harder performance penalty depending on the number of elements affected.

It's MIT licensed. Even when you prettify the code it's only 56 lines, so it's easy to audit.

Hopefully in the future you can just use Kelvyn's answer, but as of early 2024 that's Chromium only. Tim and Alex's comments would add you Firefox support for blocks of text spanning a limited number of lines (six or less for Chromium and ten or less for Firefox), but you'd still miss out on Safari, which might be an issue if many of your users use iOS. It's very well possible that by next year you might be able to use one or both of their answers though.

Until then, I think the Adopt-Words library is probably the best solution. As squarecandy pointed out, the issue with adding &nbsp; or span tags in the other answers is that

If you automated this, it could break mobile layouts if the last two words are very long. If you don't automate it, it's difficult to train non-technical editors how to implement it correctly.

I'd also add that if you don't automate it, it becomes even more of a headache once you deal with translations for your page. I agree with squarecandy that those answers are very useful in some situations (e.g. if you need to make a change in a pinch or just on one particular page) but I think Adopt-Words might be the cleanest solution for a sitewide approach: you don't have to adjust your HTML at all beyond adding something like

<script src="adopt-words.bundle.js"></script>  
<script>
    AdoptWords.init('.example *', {
      // options here
    })
</script>

and if the user doesn't have JavaScript enabled, it should still fallback gracefully.

Torpedo answered 21/2 at 0:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.