15

Is it possible to use hyphens, or soft-hyphens in CSS, in such a way that hyphens are not rendered unnecessarily?

My goal is to keep the original text as much as possible and break any words unless absolutely critical because they are too long to fit the container width.

My specific case

I want to render the text "Hi Superman" in such a way that:

If the word "Superman" is too long to fit inside a container, I want to hyphenate it in some way, for instance:

Hi Super-
man

But if the word “Superman” could fit in the container it must be rendered without hyphens

Hi
Superman

If the case above is possible ("Superman" could be written unhyphenhated) it is UNACCEPTABLE to add unnecessary hyphens like this:

Hi Super-
man

I can change the HTML however I want. I'm allowed to inject hyphens in a way that makes sense (As long as there's more than 3 letters between each hyphen it's okay. "Superma-n" is never okay)

What I've tried

I thought soft-hyphens would be the solution. So I used: Hi Super­man

But I found out that this will result in the unacceptable case of "unnecessary hyphenation" shown above.

Snippet to show the failure:

body { 
  margin-left: 30px;
}
div {
   border: solid 1px black;
}
.wide {
  border: solid 1px blue;
      width: 500px;
}
.narrow { 
  border: solid 1px red;
  width: 360px;
}
 h2 {
   font-family: 'Courier New';
    font-weight: 400;
    font-size: 87px;
  }
code {
 background-color: #eee;
}
<p> <code>Super&amp;shy;man</code> looks good in really narrow containers where the full word "Superman" would not fit</p>
<p>
<div class="narrow"><h2>Hi Super&shy;man</h2></div>

<p>But in this case the word "Superman" would fit unhypenhated on the second row. But the damn CSS decides to add hyphens anyway. Unacceptable in my case!
Again, this uses the same code as the previous example
<code>Super&amp;shy;man</code></p>
<div class="wide"><h2>Hi Super&shy;man</h2></div>

<p>I even tried adding a wordbreak tag before "Superman", like this <code>Hi &lt;wbr&gt; Super&amp;shy;man</code> but it does not help</p>
<p>  </p>
<div class="wide"><h2>Hi <wbr>Super&shy;man</h2></div>

Can I solve the example above with just CSS? (Tried different word-break properties without any success)

My guess is that this is impossible to solve this with just simple HTML and CSS due to the nature of CSS. I assume CSS just parses text line-by-line and it can never know if a word would "fit better" on the next row of text. If it finds a hyphen, it will try to fit the maximum amount of chars on the current line of text.

I want to solve this with just HTML and CSS or not at all.

Preferably:

  • No text-parsing with JavaScript.

  • I don't want to have to set different CSS-properties per div based on how wide the div is and whether I guess the text will need hyphenation or not.

  • Would prefer not to have to add wrappers around my words

  • No auto-hyphenation that can result in laughable hyphenations like "Superma-n" (However in a pinch I would be ok with auto-hyphenation as long as there's 3 chars between each hyphen. Like "Sup-erman")

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Drkawashima
  • 8,837
  • 5
  • 41
  • 52
  • 1
    I think you're going to have to accept defeat or embrace JavaScript. The way you're trying to render `Superman` is pretty much the very case that hyphenation tries to avoid. It's *supposed* to hyphenate words to keep as much on a single line as possible. You're trying to deviate from its default function. – Robert Jan 11 '18 at 14:58
  • I'm counting on having to give up. I'm not so desperate to solve this that I would do anything. I just wanted to check if someone had a simple html+css hack for this which I wasn't aware of. But I'm assuming there is none. – Drkawashima Jan 11 '18 at 15:36
  • How about this: https://jsfiddle.net/vy6ptnmj/39/ – dokgu Jan 11 '18 at 21:21
  • @uom-pgregorio: No. That's a perfect example of unacceptable "unnecessary hyphenation". Even when the container is wide enough to to fit every word in their entirety, it will break and hyphenate the longer words https://jsfiddle.net/9dmrky6y/1 My goal is to not modify the original text by adding hyphens unless it's critical because the word is too long to fit the container – Drkawashima Jan 11 '18 at 22:59

2 Answers2

4

CSS 3 includes a "hyphens" property that sets hyphenation accordingly to the specified lang attribute. But it's barely a working draft, so support is kind of not there yet.

You can set it to

  • none, so no hyphens will be shown
  • manual, so it will break words only where the special character &shy was declared, or
  • auto, which would automatically add hyphens where needed according to the localization.

Works like a charm in Firefox, Edge, and even IE, but the main issue is that webkit doesn't support the "auto" value. Except on macs and android, where it's the only value they'd accept. Yup, is that kind of weird bug.

Here's an example, make sure to check the difference between firefox and chrome, if you're running windows / linux.

p { 
  width: 55px;
  border: 1px solid black;
 }
p.none {
  -webkit-hyphens: none;
  -ms-hyphens: none;
  hyphens: none;
}
p.manual {
  -webkit-hyphens: manual;
  -ms-hyphens: manual;
  hyphens: manual;
}
p.auto {
  -webkit-hyphens: auto;
  -ms-hyphens: auto;
  hyphens: auto;
}
<ul>
    <li><code>auto</code>: hyphen where the algorithm is deciding (if needed)
    <p lang="en" class="auto">An extremelyasdasd long English word</p>
  </li> 
  <li><code>manual</code>: hyphen only at &amp;hyphen; or &amp;shy; (if needed)
    <p lang="en" class="manual">An extreme&shy;lyasd long English word</p>
  </li>
  <li><code>none</code>: no hyphen; overflow if needed
    <p lang="en" class="none">An extreme&shy;lyasd long English word</p>
  </li>
</ul>

A common workaround for the lack of "auto" support on webkit is use hyphens:auto together with work-break:break-all for webkit, so text will be hyphened on browsers that supports it and wrap without hyphens on webkit.

Facundo Corradini
  • 3,825
  • 9
  • 24
  • Well now you've got me curious. Your snippet looks exactly the same to me in Chrome and Firefox. Maybe a recent update? – cjl750 Jan 11 '18 at 15:09
  • cj750 does the first block (the one with auto and not "shy") hyphens correctly to you? If so, which platform are you using? – Facundo Corradini Jan 11 '18 at 15:13
  • First block with `hyphens: auto` hyphenates as "ex-tremely" while the last block with `hyphens: manual` hyphenates as "extreme-ly". But the important bit is that it *does* hyphenate at least. I'm looking at Chrome 63 and Firefox Developers Version 58 on a Mac. – cjl750 Jan 11 '18 at 15:17
  • @cjl750 yup, that's because the lack of support for hyphens:auto on webkit varies between platforms. I just edited the answer with clarifications. Apparently is a really, really buggy behaviour which causes webkit in Mac and an Android to **only** accept the "auto" value, while all others accept **any value but** "auto". – Facundo Corradini Jan 11 '18 at 15:19
  • Interesting! Well with that extra tidbit in there I think it's a great answer, and you were 16 seconds quicker, so I deleted mine =P – cjl750 Jan 11 '18 at 15:21
  • bummer, hate when two good similar answers get typed at the same time. Sorry 'bout that. – Facundo Corradini Jan 11 '18 at 15:24
  • Doesn't cut it. Both `hyphens: auto` and `hyphens: manual` WILL cause the unnecessary word-breaks I so badly want to avoid. (i.e words that _could_ fit on a new line would be broken and hyphenated) – Drkawashima Jan 13 '18 at 18:03
4

Use a container with display: inline-block around the long words.

body { 
  margin-left: 30px;
}
div {
   border: solid 1px black;
}
.wide {
  border: solid 1px blue;
      width: 500px;
}
.narrow { 
  border: solid 1px red;
  width: 360px;
}
 h2 {
   font-family: 'Courier New';
    font-weight: 400;
    font-size: 87px;
  }
code {
 background-color: #eee;
}

.unbreakable {display:inline-block}
<p> <code>Super&amp;shy;man</code> looks good in really narrow containers where the full word "Superman" would not fit</p>
<p>
<div class="narrow"><h2>Hi <span class="unbreakable">Super&shy;man</span></h2></div>

<p>And in this case the word "Superman" would fit unhypenhated on the second row.<br/>
This uses the same code as the previous example</p>
<div class="wide"><h2>Hi <span class="unbreakable">Super&shy;man</span></h2></div>

Edit: Oh, I did find a flaw: words that do break in the wrapper take up all of the two lines rather than allowing shorter words on the same line. In the below example, the text "man is" would have fit on one line if it hadn't been for this trick.

body {
  margin-left: 30px;
}

.narrow {
  border: solid 1px red;
  width: 360px;
}

h2 {
  font-family: 'Courier New';
  font-weight: 400;
  font-size: 87px;
}

.unbreakable {
  display: inline-block
}
<div class="narrow">
  <h2><span class="unbreakable">Super&shy;man</span> is coming.</h2>
</div>

So, no, not perfect. Sorry.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • This solution is not 100% as you would have to decide in advance which words to wrap. So it would not work in a super-responsive world where you sometimes need 8-char-words to be unbreakable and other times 12-char-words. But it's 99% good enough, since in my case I would settle for wrapping 8-char words all the time and call it a day. Having to wrap words is ugly but at least it works and is easy to implement. – Drkawashima Jan 11 '18 at 15:58
  • @Drkawashima Well, if you simply wrap all the words containing `­` chars, you won't have to decide which to wrap on a case-by-case basis. – Mr Lister Jan 11 '18 at 20:41
  • I just thought of a real flaw that makes this solution less than perfect though; see added edit. Sorry. – Mr Lister Jan 11 '18 at 20:50
  • I see. But I can live with that problem. My main requirement is to avoid unnecessarily changing words by adding hyphens. Pushing a word down to a new line is not a huge concern. – Drkawashima Jan 11 '18 at 23:28
  • I'm gonna mark this as the answer cause my main goal was to only add hyphens when truly necessary, and this does the job. – Drkawashima Jan 13 '18 at 18:05