Learning the CSS Selectors: from N00b to Ninja

Authoring HTML and CSS will never be as complex as programming. HTML is, after all, a markup language, and CSS is merely a stylesheet language. CSS is simple enough that the first two or three hours of writing it won’t be that different from your next 500 or 1000 hours. You learn the basic ways of selecting elements, and then you learn what properties and values you can assign. Most of your energies go into learning the properties and values without really exploring your selectors any more. The selectors, though, are where you move from n00b to novice, novice to ninja, and eventually, ninja to sorcerer. So, here’s a break-down of CSS selectors that won’t suck. 

Specificity in under 100 1000 words

  1. There are four… “levels” of specificity; one level never interferes with the other
  2. There are three basic methods of selection within a stylesheet
  3. Every time you specify an element within its level, you add a value of 1 to its specificity

Some erroneously liken it to the decimal system where an element is 1, a class is 10, an ID 100 and inline style 1000.  But that’s not accurate. A ten class selector doesn’t match or beat a single ID. Ever.

.this .is .a .very .long .and  .really .useless .selection .method  ≠ #thisIsStillAMorePowerfulSelector

HTML CSS specificity
<h1> h1{} 0,0,0,1
<h1 class=”heading”> .heading 0,0,1,0
<h1 id=”header”> #header{} 0,1,0,0
<h1 style=”color:blue”> 1,0,0,0

So here’s our winners of specificity, from least to greatest

HTML CSS specificity
<h1><strong></strong></h1>  h1 strong{} 0,0,0,2
<h1 class=”heading”>  h1.heading{} 0,0,1,1
<h1 class=”heading”><strong class=”bigger”>  .heading .bigger{} 0,0,2,0
<h1 id=”header”>  #header{} 0,1,0,0
<h1 id=”header”><strong id=”bigger”>  #header #bigger{} 0,2,0,0
<h1 style=”color:blue”> 1,0,0,0


CSS Selectors: N00b level

The Super Noob approach

Your noob level of writing CSS selectors is pretty standard. It’s how the majority of CSS is written and it works flawlessly probably 40% – 50% of the time. Your CSS might  look like this for links and a navigation:

a:hover, a:focus{color:gray}
nav ul li a{color:orange}
nav ul li a:hover, nav ul li a:focus{color:red}

And then you get less N00by

Alas, you get tired of writing out all those elements, so it’s time to get classy. Your navigation styles and links might look like this. You’re using a basic class and then adding an element after it; this is a general descendant selector.

a{color:blue} a:hover, a:focus{color:gray}
.nav a{color:orange}
.nav a {color:gray}

And then you’ll occasionally shout, “eff it” after fighting with four navigations, and that’s when you pull out your trump card: the ID paired with a general descendant.

#leftSideBar a{color:#c0ffee}

CSS Selectors: Novice Level

Hidden levels are discovered

At some point it suddenly clicks that you can combine your selectors for extra specificity power. It’s like you’ve discovered hidden levels in Super Mario Bros 3. No more general descendants or whatnot, you’re getting pretty specific by chaining the element directly to the class name.

div#leftSidebar a.navLink{color:fuschia}

CSS Selectors: Master level

Somewhere along the way, you trip over pseudo classes and pseudo elements.You’ve one-upped your powers and are shooting fireballs at the web. You’re generating content, pulling fancy tricks with navigation, and generally unafraid of the web.

What stinks is that once you’ve drifted into these pseudo elements and pseudo classes, you’re also constantly on your toes for IE7 and IE8. You never know when one of them will appear out of nowhere to ruin your day. But, alas, you enjoy the power.

.nav ul  li:first-child{border-bottom: 1px solid black}
div.chicken:before{content:"The Egg"}

The pseudo elements are the bonus points on account that they can really become extra elements in the DOM.  The pseudo classes like :first-child are so nice because you find nifty ways to resolve spacing and margin problems:

article{ margin: 0 0 0 1em}
article:first-child{margin: 0 0 0 0}

And then you gain powers of invincibility

Somehow you trip over descendant, adjacent, and non-adjacent sibling selectors. At this point, you own the HTML page and it fears you.

hgroup > h3{margin-top: 0} /*h3 is the first descendant of hgroup */
p + h3{margin-top: 1em;} /*only when h3 comes immediately after paragraph in the parent element */
img ~ h3 {margin-top: 2em} /*for any .heading that will ever come after .mainHeading in  the parent element*/

With power comes responsibility

Up until the descendant selectors, we had a risk of what I’d call “specificity bloat”. Essentially, it’s when you start adding more classes or even IDs just to overpower earlier styles. As a rule of thumb, I think it’s best to avoid increasing specificity solely to undo earlier styles. The “family” selectors like direct descendants, adjacent siblings, and non-adjacent siblings make it easier for you to deal with actual rules of design.

Take this little bit o’ CSS and assume you have an h3, and h3 that’s used with an h2, an h3 occurring with an hgroup, and an h3 that’s in an hgroup somewhere after a table.

h3{margin-top: .5em}
.content h3{margin-top: .5em}{margin:0}
article.content hgroup h3 {margin-top: 1em}
article.withTable.withHgroup hgroup h3 {margin-top:2em}

The challenge you have with the previous example is that your h3’s margin depends on classes and you’re starting with a higher specificity for your content. You’ll end up writing CSS rules to over-write margin rules and more to put them back.  Instead, you can start with the main rules, and then use the “family” selectors for handling your edge-cases:

h3{margin-top: .5em} /*removed .content h3 b/c the default rule applies */
h2 + h3 {margin-top:0} /*spec: 0, 0, 0, 2 */
hgroup > h3 {margin-top: 1em} /*spec: 0, 0, 0, 2 */
article table ~ hgroup > h3 {margin-top: 2em;} /*spec  0,0,0,4 */

CSS Selectors: Ninja Level

The “family” selectors are when you finally master CSS. You have full control over the major ways that you can select items and you can really drill down to specific items. But, it’s when you discover the attribute selector and then start chaining pseudo-classes with the “family” selectors that the insanity begins:

a[href=""]{color: orange} /*link to specifically */
a:hover{text-decoration:underline; color: navy}
a[href=""]:hover:before{content: "going to google"}
form:hover > input{border: 1px solid blue} /*change the input only when you hover over the form */
form:hover > input:hover{border: 2px solid blue} /*hovering the form AND hovering the input */
form:hover > input:hover ~ input[type="submit"]{background: green} /*hovering the form and the input, and then selecting the submit button */

The attribute selector is so insanely awesome, it deserves its own blog post. I’ll suffice to say that you’re at Ninja level once you’re mixing the attribute with descendant selectors. You’re able to create levels of feedback and you’re able to create distinct set of logic within your design.