I spend my spare time combining the luxury life of having no kids and a wonderful girlfriend with the agonizing pressure of blogging under my Onderhond monicker. As a front-end developer I am raised and nurtured at Internet Architects, a Belgian company investing a lot of time and resources in making the web a better place Niels is a DZone MVB and is not an employee of DZone and has posted 109 posts at DZone. You can read more from them at their website. View Full User Profile

CSS Nesting Specifics - When CSS Misbehaves

06.27.2008
| 32078 views |
  • submit to reddit

There are plenty of css bugs one can write about. Some are worth tracking because they are annoying and have a huge impact on our every day work, others are interesting because they haven't received too much attention yet. But most interesting are the ones that allow us to touch upon deeper, underlying problems. This article will focus on a css issue that will allow us to do just that.

The issue in itself is pretty obvious and easy to understand. It's not really bug, but more like an unfortunate combination of several factors. What's more interesting though, is the way it illustrates the difference between just working with css and fully understanding css. And at its very core we find an even bigger issue, plaguing many computer languages and programs. But first things first, let's take a look at the issue at hand.

The basic code

ul li {color:red;}  
ol li {color:blue;}

You don't need much knowledge of css to understand the statements above. The first rule styles an unordered list and makes the text inside the li tags blue. The second rule styles an ordered list and makes the text inside the li tags red. Rules like these are pretty common when you need to deliver default styles to be used within free content areas (usually coming from a cms where the content is inserted through some kind of rich text editor).

But if you assume these two statements will cover your list styling, you're in for a treat. It won't take long before content editors will start nesting lists and an often overlooked issue will roar its head. When an unordered list is nested inside an ordered list, the list items of both lists will turn out blue. Not really what we initially expected.

The problem

So what happened? Well, when the unordered list was nested inside the ordered list, the list item of the unordered list suddenly became part of the ordered list. We used the space combinator in our css selector to define the ordered list, meaning that every nested li element inside the ol tag will be targeted, no matter the depth or inclusion of other tags. As the specificity of both rules is the same, the last declaration in line counts, so the list items of the unordered list (which are now also part of the ordered list) will be colored blue. It's annoying but completely normal.

x z {...}  
y z {...}

The css code above gives a little abstraction of the problem. This issue hits when three basic conditions are met:

  1. both components can be nested inside each other
  2. two css selectors end with the same element (z), but start with different elements (x and y)
  3. both css rules have equal specificity

Any solutions?

There are two main solutions to work around this issue, but none of them is actually very useful.

/* solution 1 - nesting declarations */} 
ul li ol li {color:blue;}
ol li ul li {color:red;}

The first solution is to write css rules for all possible nestings. This is the safest solution but gives you no flexibility whatsoever. Whenever a content owner decides to make a third nesting, you're out of luck again. To see a popular example of this technique you can check out the Yahoo grids, which have similar nesting problems. If you check out their base css, you'll notice that they only support two levels of nestings. In addition, you can put theoretical restrictions to the nestings of course, telling content owners they can't have three nestings. When working with big clients, that's not really something you'd like to do.

/* solution 2 - the '>' combinator */}  
ul>li {color:red;}
ol>li {color:blue;}

There's a second solution, which I guess would be the preferred solution by those who conceived the css specs. To prevent issues like these from happening, you should make use of the child combinator ('>'). By using this, you'll only target the li tags that are nested one level below the ul/ol tag. It's a nice solution, although it does hamper css flexibility again. It works okay with lists because the only allowed tag below the ul/ol tag is the li tag. In a broader sense, it can really mess up a design when you insert a simple wrapper div, possibly invalidating a whole string of css selectors.

What's even worse though, IE6 doesn't support the child combinator. So as long as we have to take IE6 into account, our second solution has little real world value.

So why are we falling so easily for this issue?

Taking all things into account, it's no real surprise we fall so easily for this issue, especially when we are starting out with css. Combinators are often forgotten simply because there is little to no general support for them. We learn css on a "what's useful and what's not" basis, we ignore the rest. Looking at the common syntax of css selectors, we see that the only combinator used is the space combinator. And so, at first, we don't even realize the space is actually a meaningful part of the css selector, which can be substituted with a list of alternatives.

Only when we start digging deeper and deeper into the css syntax and possibilities do we find out about the range of combinator we have at our disposal. By that time, the idea that css selectors are based on a singular combinator is so well-rooted that it's hard to keep the side effects of the space combinator in mind all the time. So once in a while, we are fooled by a simple issue like this.

Of course, we have to thank shabby browser support for this, as even now we cannot safely use any other combinator besides the space. IE6 supports none of the interesting ones and as long as the old monster is around we'll be left with issues like these lurking about. When you're starting out with css, you start with the tools available, and sadly the range of combinators is not one of those tools. So again, this is a "blame IE6" situation.

Looking further

Still, browser support isn't the real core of the issue. As I already explained with the second solution, the child combinator could solve our problem but would put a serious strain on the flexibility of our css. So what is it all about then?

What's really going on here is a difference in interpretation from the human mind and the way css is applied. It's in fact the following little rule that's messing things up: in case two css selectors have equal specificity, the last one will be applied. This makes sense from a logical point of view (i.e. the easiest and safest way to implement), but not from a human point of view.

Looking back at our first solution, we've styled an ordered list and an unordered list. At least, we think we did, that's how we perceive the selectors. In reality, our css doesn't know anything about this, it only matches selectors to html. We silently suppose that css is smart enough to recognize the ordered list inside the unordered list, but that's where we are mistaken. The human mind thinks in components, while css just matches selectors according to a fixed set of rules.

Instead of figuring out a system where styled components are recognized as such (which is quite hard I suppose), the implementation was made a lot easier by saying that the last selector in line (in case of equal specificity) will take preference. Exactly that is causing us trouble when we are nesting components and why this issue plagues us from time to time.

Conclusion

No matter what solution you come up with, if you really want to tackle this issue you either need to completely change your mind set to adapt to the way css is applied, or you need to rework the css rules to match the human mind. Neither solution is possible I'm afraid, so the best thing to do is to stay alert and avoid problems like these by trying to remember they can happen. It's not much, but you get used to it after a while.

Published at DZone with permission of Niels Matthijs, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)