39

Some days I swear I'm going mad. This is one of those days. I thought my CSS was fairly straight-forward here, but it just doesn't seem to be working. What am I missing?

My CSS looks like this:

ul > li {
    text-decoration: none;
}
ul > li.u {
    text-decoration: underline;
}
ul > li > ul > li {
    text-decoration: none;
}
ul > li > ul > li.u {
    text-decoration: underline;
}

And my HTML looks like this:

<ul>
  <li>Should not be underlined</li>
  <li class="u">Should be underlined
    <ul>
      <li>Should not be underlined</li>
      <li class="u">Should be underlined</li>
    </ul>
  </li>
</ul>

Yet it comes up like this:

Image

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
soapergem
  • 9,263
  • 18
  • 96
  • 152

7 Answers7

47

text-decoration does not behave the same as other font/text related styling like font-weight. Applying text-decoration will affect all nested elements as well.

Check this out: http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration

Excerpt:

Text decorations on inline boxes are drawn across the entire element, going across any descendant elements without paying any attention to their presence. The 'text-decoration' property on descendant elements cannot have any effect on the decoration of the element
. . . .
Some user agents have implemented text-decoration by propagating the decoration to the descendant elements as opposed to simply drawing the decoration through the elements as described above. This was arguably allowed by the looser wording in CSS2.

I've got the info from: http://csscreator.com/node/14951

o.k.w
  • 25,490
  • 6
  • 66
  • 63
  • 5
    So essentially it can't be done; instead I'll have to make sure never to set that property on a parent li, and instead include a there or somesuch. It seems like a stupid and arbitrary rule to include in the CSS spec; it's so counter-intuitive. – soapergem Dec 01 '09 at 15:01
  • 2
    Sadly speaking, that is true. To be honest, if not for your question, I wouldn't have found that out as well :P – o.k.w Dec 01 '09 at 23:44
  • 1
    @o.k.w , I just faced the same problem, but I guess you can still apply text-decorations on nested elements by converting them to block elements. http://stackoverflow.com/questions/10478919/css-line-through-is-not-disabled-on-nested-span/10478962#comment13540343_10478962 – KBN May 07 '12 at 08:41
  • 27
    Saw this solution on another question, use `display: inline-block;` to clear out the item's `text-decoration`. http://stackoverflow.com/a/19529256/1977420 – mikegertrudes Oct 22 '13 at 22:10
  • 1
    This answer is outdated; Oriol’s answer shows how to override the underlining for inner elements. – Jukka K. Korpela Nov 25 '14 at 23:02
33

You get rid of text-decoration applied to a parent element in those cases:

  • Out-of-flow elements, such as floated and absolutely positioned ones

    li {
      float: left; /* Avoid text-decoration propagation from ul */
      clear: both; /* One li per line */
    }
    ul { overflow: hidden; } /* Clearfix */
    

    ul {
      overflow: hidden; /* Clearfix */
    }
    li {
      float: left; /* Avoid text-decoration propagation from ul */
      clear: both; /* One li per line */
    }
    li.u {
      text-decoration: underline;
    }
    <ul>
      <li>Should not be underlined</li>
      <li class="u">Should be underlined
        <ul>
          <li>Should not be underlined</li>
          <li class="u">Should be underlined</li>
        </ul>
      </li>
    </ul>
  • Atomic inline-level elements, such as inline blocks and inline tables

    But if you use li{display:inline-block}, then you don't have bullets (you lose display:list-item) and the items appear one next to the others.

    Then, to have one item per line, you can use

    li {
      display: inline-block; /* Avoid text-decoration propagation from ul */
      width: 100%;           /* One li per line */
    }
    

    And to add the bullets, you can use ::before pseudo-elements. However, bullets shouldn't be underlined, so you will need to take them out-of-flow or make them atomic inline-level too.

    li {
      display: inline-block; /* Avoid text-decoration propagation from ul */
      width: 100%;           /* One li per line */
    }
    li:before {
      content: '• ';         /* Insert bullet */
      display: inline-block; /* Avoid text-decoration propagation from li */
      white-space: pre-wrap; /* Don't collapse the whitespace */
    }
    li.u {
      text-decoration: underline;
    }
    <ul>
      <li>Should not be underlined</li>
      <li class="u">Should be underlined
        <ul>
          <li>Should not be underlined</li>
          <li class="u">Should be underlined</li>
        </ul>
      </li>
    </ul>

    li {
      display: inline-block; /* Avoid text-decoration propagation from ul */
      width: 100%;           /* One li per line */
    }
    li:before {
      content: '•';          /* Insert bullet */
      position: absolute;    /* Avoid text-decoration propagation from li */
      margin-left: -.75em;
    }
    li.u {
      text-decoration: underline;
    }
    <ul>
      <li>Should not be underlined</li>
      <li class="u">Should be underlined
        <ul>
          <li>Should not be underlined</li>
          <li class="u">Should be underlined</li>
        </ul>
      </li>
    </ul>

This behavior is specified in CSS 2.1 and CSS Text Decoration Module Level 3:

Note that text decorations are not propagated to any out-of-flow descendants, nor to the contents of atomic inline-level descendants such as inline blocks and inline tables.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • you can use `inline:block` on a div inside the `li` so you don't loose the bullets – Omu Oct 23 '17 at 15:14
2

The reason you´re seeing what you're seeing is that your rule

ul > li.u

takes preference over:

ul > li > ul > li

as a class is specified and that has more weight than the element selectors together.

Edit: What you could try is:

.u ul {
        text-decoration: none;
}
.u {
        text-decoration: underline;
}

and play around with that (perhaps you will have to use li.u instead of just .u).

However, depending on the content you might want to wrap the underlined parts in q, em or strong tags and style these tags instead of using a class. That way you would be describing your content as well as styling it.

jeroen
  • 91,079
  • 21
  • 114
  • 132
  • This is correct, the most specific rule in css will have the most weight in the cascading appliance of those rules, #id is most specific, then .class, then element selectors. – Colin Dec 01 '09 at 00:57
  • I saw a list somewhere once with the exact values to calculate the weight of a rule. Can´t find it now though... – jeroen Dec 01 '09 at 00:58
  • I understand, but fundamentally my question is how I go about overriding that underline property that's being inherited and setting it back to none, as it seems to be ignoring that currently, even if I add !important to the more specific none rule. – soapergem Dec 01 '09 at 06:08
2

o.k.w.'s answer above explains perfectly why you can't do what you are asking without some other changes. No, you're not going mad!

Possible workarounds:

  • try border-bottom?
  • wrap the text you want underlined in a span class="u" tag? (to prevent the text-decoration from decorating nested elements)
  • if you aren't able to change the markup, you could add some scripting to accomplish the same as my previous suggestion.

Best of luck!

Community
  • 1
  • 1
Funka
  • 4,258
  • 2
  • 24
  • 27
  • I often simplify/modify the examples that I give in questions to make my explanation as clear as possible. In reality, I'll actually be dealing with text-decoration: line-through, and none of the border properties really line up with that. It looks like either extra span tags or JavaScript is the solution. Both seem so inelegant though; this seems like this is a "bug" in the CSS spec more than anything. – soapergem Dec 01 '09 at 06:05
0

I ran into a similar issue when using an external theme/CSS, so I couldn't modify it to remove the text-decoration: none;. In my case, I had a child element with a link, but the link wasn't being underlined as expected. I tried using display: inline-block; as others mentioned, but it has no effect.

What worked for me was overriding the text-decoration as you had done, but also including !important to force it to override the parent's text-decoration.

// Defined in an external CSS, so I couldn't modify it.
.footer {
    text-decoration: none;
}

// In my CSS file.
.footer a {
    text-decoration: underline !important;
}

So for your particular example, I imagine this may do the trick (I did not test it to confirm):

ul > li > ul > li {
    text-decoration: none !important;
}
deadlydog
  • 22,611
  • 14
  • 112
  • 118
  • I tried this but it didn't work. Could you upload a snippet at all? – Math is Hard Dec 23 '20 at 19:27
  • @MathisHard I think the snippet I provided here is easier to understand, but [here's where I'm using it in my blog](https://github.com/deadlydog/deadlydog.github.io/blob/master/assets/css/main.scss#L241) if you want to see a real example in action. – deadlydog Dec 25 '20 at 17:15
0

The cleanest approach I’ve found is just to set the underline to the background’s colour.

-3
.u {text-decoration: underline;}
James
  • 46
  • 4
  • 2
    Doesn't work, since the child list will have all its elements underlined. See http://jsbin.com/iweri to see how this would look. – Vegard Larsen Dec 01 '09 at 00:53
  • See Andrews code it's backwards of mine and it works. Weird that mine does not :) – James Dec 01 '09 at 01:11
  • OP already uses equivalent code, and there is no explanation. This is not an answer. – Oriol Nov 26 '15 at 13:19