4

When using word-spacing in an underlined element, if the element contains spans, spaces are not correctly underlined making it really ugly...

Is there a work around for that?

p {
  text-decoration: underline;
  word-spacing: 1em;
}
<p>
  <span>test</span> <span>test</span>
</p>
<p>
  test test
</p>

Edit:

I found this trick that is almost satisfying for my use case:

p {
  text-decoration-line: underline;
}
span:after {
  content: ' ';
  letter-spacing: 1em;
}
<p>
  <span>test</span>
  <span>test</span>
</p>

The issue is that I would like to use it with a Zero-width space, like \u200B, but for some reason, it doesn't work. Maybe I'm doing it wrong...

p {
  text-decoration-line: underline;
}
span:after {
  content: '\200B';
  letter-spacing: 1em;
}
<p>
  <span>test</span>
  <span>test</span>
</p>
Sharcoux
  • 5,546
  • 7
  • 45
  • 78
  • as side note, if you try to hightlight the text you will understand that it's somehow logical – Temani Afif Aug 26 '18 at 14:50
  • @TemaniAfif - I don't see what's logical about it. And Firefox and Edge don't think so either. Looks like a Chrome bug to me. – Alohci Aug 26 '18 at 14:56
  • @Alohci when you highlight the text you get only the underlined one (at least on chrome), this is *logical* for me ... the highlight behave the same as the underline – Temani Afif Aug 26 '18 at 15:01
  • @Alohci it's seems the same logic for FF .. the hightlight match the underline but who is wrong/right I don't know .. – Temani Afif Aug 26 '18 at 15:05
  • @Alohci ok, it's seems a bad behavior within chrome, whitespace should be included, I saw this in a pevious question : https://stackoverflow.com/questions/48549803/underline-breaks-with-spans-have-padding-or-margin .. so might be something to do with word-spacing – Temani Afif Aug 26 '18 at 15:10
  • I have undeleted my answer is you are still looking for workaround but I think this is a bug – Temani Afif Aug 26 '18 at 20:02

2 Answers2

1

A workaround is to remove the white space between the spans and use a hack with pseudo element to simulate word-spacing. It works but it remain a hacky solution:

p {
  text-decoration: underline;
  word-spacing: 1em;
  display:flex; /*remove white space*/
}

span:not(:last-child)::after {
  content:"\00a0";
}
<p>
  <span>test</span> <span>test</span>
</p>
<p>
  test test
</p>

If you will only have spans you can do it like this:

p {
  text-decoration: underline;
}

span:not(:last-child)::after {
  content:" ";
  letter-spacing: 1em;
}
<p>
  <span>test</span> <span>test</span>
</p>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Not bad. Still a little troublesome to remove the spaces between the spans in a text editor though. – Sharcoux Aug 27 '18 at 08:07
  • @Sharcoux yes it will work only for the first one ;) but I tried to make a generic solution that works in all the cases even without span :) – Temani Afif Aug 27 '18 at 08:40
  • @Sharcoux here is my solution with letter spacing ;) https://stackoverflow.com/questions/48549803/underline-breaks-with-spans-have-padding-or-margin – Temani Afif Aug 27 '18 at 08:41
  • Yes. Perfect. The use of flex is really funny though. The only issue remaining now is for text elements that are not inside a span, but in my case it is not problematic. By the way, afterthought, your first solution is better in this case. – Sharcoux Aug 27 '18 at 08:47
1

Looking for the right workaround...

A quick workaround to fix the issue in Chrome would be to replace the spaces with &nbsp;, but that will work only for a single line of text. If the text doesn't fit in a single line, it will overflow the parent element instead of breaking into multiple lines:

p {  
  text-decoration: underline;
  word-spacing: 1em;
  width: 33.33333333%;
  border-right: 3px solid black;
  padding: 10px;
  margin: 0;
}
<p>
  <span>test</span>&nbsp;<span>test</span>
</p>

<p>
  <span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>&nbsp;<span>test</span>
</p>

Another workaround for that would be to also add a zero-width space to the mix: &nbsp;&#8203;, but you can see that the underline now extends before or after the text due to the &nbsp; being displayed and underlined as well:

p {  
  text-decoration: underline;
  word-spacing: 1em;
  width: 33.33333333%;
  border-right: 3px solid black;
  padding: 10px;
  margin: 0;
}
<p>
  <span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>&nbsp;&#8203;<span>test</span>
</p>

<p>
  <span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>&#8203;&nbsp;<span>test</span>
</p>

✨ Space + Zero-Width Space to the rescue

Looks like we are getting somewhere... As we don't want the &nbsp; to be displayed when it's at the end or the beginning of a line, let's use a normal space and a zero-width space: &#8203;:

p {  
  text-decoration: underline;
  word-spacing: 1em;
  width: 33.33333333%;
  border-right: 3px solid black;
  padding: 10px;
  margin: 0;
}
<p>
  <span>test</span>&#8203; <span>test</span>
</p>

<p>
  <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>&#8203; <span>test</span>
</p>

Using additional HTML elements to get additional underline customization/styling options

Another option is to use a wrapping element to create the underline using a border or a box-shadow, which would also allow you to have some additional customization/styling options, as you can see in these examples:

p {
  word-spacing: 1em;
  width: 33.33333333%;
  border-right: 3px solid black;
  padding: 10px;
  margin: 0;
  float: left;
  box-sizing: border-box;
}

p:nth-child(3n) {
  border: none;
}

.fakeUnderline1 {
  border-bottom: 2px solid black;
  transition: border-bottom ease-in 75ms;
}

.fakeUnderline1:hover {
  border-bottom-color: cyan;
}

.fakeUnderline2 {
  border-bottom: 1px solid black;
  transition: border-bottom ease-in 75ms;
}

.fakeUnderline2:hover {
  border-bottom-width: 3px;
}

.fakeUnderline3 {
  box-shadow: inset 0 -2px 0 0 yellow;
  transition: box-shadow ease-in 75ms;
}

.fakeUnderline3:hover {
  box-shadow: inset 0 -16px 0 0 yellow;
}

.fakeUnderline4 {
  overflow: hidden;
  box-shadow: 0 2px 2px -3px red;
  transition: box-shadow ease-in 75ms;
}

.fakeUnderline4:hover {
  box-shadow: 0 5px 5px -5px red;
}

.fakeUnderline5 {
  border-bottom: 1px dotted black;
  transition: border-bottom ease-in 75ms;
}

.fakeUnderline5:hover {
  border-bottom-style: solid;
}

.fakeUnderline6 {
  border-bottom: 2px solid blue;
  box-shadow: inset 0 -2px 0 0 red;
}

.fakeUnderline6:hover {
  border-bottom-color: red;
  box-shadow: inset 0 -2px 0 0 blue;
}

.fakeUnderline7 {
  text-decoration: underline;
}
<p>
  <span class="fakeUnderline1">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

<p>
  <span class="fakeUnderline2">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

<p>
  <span class="fakeUnderline3">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

<p>
  <span class="fakeUnderline4">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

<p>
  <span class="fakeUnderline5">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

<p>
  <span class="fakeUnderline6">
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span> <span>test</span> <span>test</span>
    <span>test</span> <span>test</span>
  </span>
</p>

Even though the question is not about underline styling, note that if you only need some basic underline styling options and browser support is not an issue, you could use these properties instead of the solution above:

More complex behaviour would be available once these experimental features are supported:

Danziger
  • 19,628
  • 4
  • 53
  • 83
  • Your answer is great, but not very satisfying. I'm not really enthusiastic about changing the dom. Moreover, my use case being a text editor, replacing undeline by border-bottom can be very troublesome... – Sharcoux Aug 26 '18 at 16:24
  • @Sharcoux I have updated the answer. A workaround for the issue on Chrome would be to use ` ` instead of a space. – Danziger Aug 26 '18 at 16:56
  • 1
    Ahah that's a funny hack. The only issue is that your paragraph will never break now... ;) – Sharcoux Aug 26 '18 at 19:30
  • By the way, I don't see how I am supposed to use the new text-decoration-xxx options to fix my issue... They will still break between my spans when using word-spacing. – Sharcoux Aug 26 '18 at 19:32
  • @Sharcoux You are right, that solution only works for a single line of text. Check the updated answer, looks like there's a better similar workaround . Regarding the `text-decoration-` properties, that's just there in case someone finds this post while looking for underline customization options (_Even though the question is not about underline styling..._). – Danziger Aug 26 '18 at 20:27
  • I like your zero-width solution. Is it possible to use :after with this solution? – Sharcoux Aug 27 '18 at 08:32