CSS Kind Of Hates You

Or Alternatively – "Exploring CSS Hierarchies"

CSS hierarchies can be a tricky things at times. It can be good in the right hands, or a painful ordeal in everyone else's. In this article, I will go over three different types of CSS hierarchies, and how to avoid the pitfalls associated with them.h2 Selector Hierarchy

Selector Hierarchy

This is probably the most talked about instance of CSS hierarchy in recent years. It is the reason why many developers avoid using IDs in their styles like it's a plague. I'll admit, I am no fan of the ID selector, but it gets a bum rap. The CSS community has made it into an infamous monster, of sorts. A monster so great, it ate all the other infamous monsters, The Internet Explorer Monster Browser included, to absorb their powers. But like most movie monsters, it's not evil; just misunderstood. So why is the ID selector so hated?

It's The Most Powerful Of All Selectors

The ID selector ranks at the very top of the selectors and enforces its will, regardless of context. There is no combination of CSS selectors that can overwrite an ID that doesn't include the ID itself. The ID is king. Consider this example:

You have a div with the class "text" on it. Inside .text, there is a paragraph with an ID of "intro" and a class of "blue".

#intro{
   font-size: 1.2em;
   color: red;
}
.blue{
   color: blue;
}
p.blue{
   color blue;
}
.text > div.blue{
   color: blue;
}

What color do you think #intro will be? #intro will be red. Despite it having no descendant tags and being at the top of the sheet, #intro will be red. If decedent tags and cascading hierarchy won't work, how do you change #intro's color? You can:

    Change the ID styles. This is not a great solution. What if there are other #intros on other pages?

    Create a stronger combination of selectors. This is probably the best solution. A super selector like #red.blue will overwrite #red. It can get a bit messy after a while and defeats the purpose of having a class, however it works.

    Add an !important to one of the would be overwriting styles. However, !important should only be used in the most dire of circumstances. Repeat after me: "!important should only be used in the most dire of circumstances". Very good.

If IDs are too powerful, what should we use? Some developers simply ignore them. They use a mixture of element tags and classes. Using this method, everything can be overwritten easily if needed:

.intro{
   color: red;
}
.blue{
   color: blue;
}

.blue will overwrite .intro's color by simple hierarchy. However, you can also use element tags with descenders to overwrite .intro's styles, without the additional class:

.intro{
   color: red;
}
.text p{
   color: blue;
}

When it comes to the ID selector, I follow the Uncle Ben method:

With great power comes great responsibility.
-Uncle Ben

Use IDs if you need them, but be very, very careful with them.

It Cannot Be Reused In A Page

Personally, this is the most bothersome problem of IDs for me. IDs can, semantically speaking, only be used once on a page. Most browsers will still render them multiple times, but they were only meant to be used once. Your HTML won't validate.

It Forces Us To Use The Shift Key

This one just might be me. Dots take less effort to write than pound signs. #fact

Z-index Inheritance

In 2012, Phillip Walton wrote about the strange things that can affect your stacking order. I won't get into specifics like Phillip does, but I'll go over some tips so you won't be surprised.

Natural Hierarchy Is Pretty Simple... Mostly

If you're not utilizing positioning in your code, the natural stacking order is pretty simple. It's in the same order of position in the HTML. This is also true for SVGs. It's when you begin adding the position property to elements that the natural stacking order changes. The positioned elements will take a higher stacking order over non-positioned elements. Positioned elements with a higher defined z-index will be higher in the z-index. This, however, is not true for SVGs. SVGs don't recognize z-index values and will always follow its own self-contained natural hierarchy.

Stacking Context Can Either Be Your Greatest Ally Or Your Worst Foe

A stacking context is a stacking order inside a stacking order. That's to say, if you have a nav element inside a header element, you can create a whole new stacking order inside the header element by simply placing a z-index value on it. Let say the header's z-index is set to 7 and the nav is set to 2. The nav element will still appear at the 7 level, because it's a child of the header, but it will appear at the 2nd level inside it. So if you add a div and position it at z-index:99, it will appear higher than the nav element, but still on the 7 global stacking order.

Weird Things Can Affect Your Stacking Order

Most notably, opacity. Using the natural hierarchy, or by not defining a z-index value, opacity can mess with your z-index. Elements with an opacity value of less than 1 will appear lower in the stacking order than other elements on the same z-index with no opacity defined or opacity:1;. Despite this, there is no real discernable change in the z-index order. If you have 3 divs, two are set to z-index:5 and one of those is set to opacity: .9, there is no way to separate the two in the stacking order with the third div. It seems like the first div is 5.5, and the one with opacity: .5 is set to 5.1. There is no way to get in-between that.

Negative Z-Indexes Are Awesome

In addition to going up, you can go down! Anything with a z-index below 0 will appear beneath the natural order, positive stacking contexts notwithstanding!

The only advice I have about managing z-indexes effectively is to avoid complex stacking contexts and implement a z-index scale!

Transform Inheritance

This isn't one that's talked about often, but when you do encounter it, it's pretty debilitating. Using the transform property on the parent of a position: fixed element will unfix the fixed element. Let that sink in. It will unfix it. A few months ago, I was building a sidedraw navigation. I decided to build my HTML like so:

<div class="track">
   <section class="side-navigation">...</section>
   <section class="main-content">...</section>
</div>

It's a pretty simple set up. .track was going to be my tracking element while .main-content would exist to hold all the content normally held in a body or wrapper. .main-content would be set to 100%, while the .side-navigation would be set to 20%, then positioned: absolute and transform: translateX(-100%). This would ensure the side draw navigation would be invisible until .track had moved over 20%. I decided to use transform for .track's animation, because of that sweet 60FPS. One problem: The hamburger button was fixed inside .main-content. Once the hamburger was clicked and the track did it's thing, it'd disappear. Gone. It lost its sticky. Luckily, I was able to move the menu button outside of .track overcome this issue, but it's a weird behavior. From my testing, all transform properties seem to cause this effect.

Conclusion

And there you have it. My tips, tricks and warnings when working with three different type of inheritance and hierarchy in CSS. If you have any questions, concerns, or contributions to this article, feel free to reach out to me on twitter. Thanks for reading and happy coding!

Comments