Tuesday, June 23, 2009

Be Forewarned: The Cascade's Got Reach

I'm going to cut right to the chase and talk about a selector that styles the numerical values which label the bars of a graph. The rule includes grey colored text that is offset 25 pixels above where the span element would have been in the normal flow.

/* ---------------------------------------------------------------------------------- */
.bargraph ul.bars li span {
color: #7c7c7c;
position: relative;
top: -25px;
}

The markup before the span looked like:
/* ---------------------------------------------------------------------------------- */
<li class="bar4 greenbar" style="height: 80px;">40</li>

Now instead you enclose "40" in a pair of beginning and end span tags to take advantage of the new rule that you just created:
/* ---------------------------------------------------------------------------------- */
<li class="bar4 greenbar" style="height: 80px;"><span>40</span></li>

The spans will force the number to appear above the rectangular bar instead of inside ie compare the green bar with the other four whose markup do not include spans.


White text tends to blend into yellow background so let's say we'd also like to write the number into the last bar in a darker text color. The orange bar comes right after the green that we just worked on. You create a new span class that says,
/* ---------------------------------------------------------------------------------- */
.bargraph ul.bars li span.greypen {
color: #7c7c7c;
}

You update the markup to reflect the new changes:
/* ---------------------------------------------------------------------------------- */
<li class="bar5 orangebar" style="height: 160px;"><span class="greypen">80</span></li>

expecting to merely change the color of the font. You do not want to move the text outside the bar but contrary to what you thought, the number is actually printed right on top of the orange bar just like its green neighbor.


So what's going on since you did not say anything about a new position? Well for lack of a better term, your new span fell under the spell of the cascade. This is what's called the cascading effect in CSS, when several different styles compete for control of elements on a page, the style information is passed down. And unless it is overwritten by a rule that has a higher specifity, or occurs later on in the style sheets, it will live on.

In this example, specifity is not an issue since we have a highly specific selector in ".bargraph ul.bars li span.greypen" which is an exact match, but the browser will also find and apply rules from a partial match which is the ".bargraph ul.bars li span" selector. So then, the plain span selector specifying a relative position and a 25 pixel offset outside the li element gets applied to the orange bar li element.

If you look at the computed styles that the browser keeps, you will see values being assigned for various properties under "text", "background", "box model", and "layout" categories. Position is under layout and top is under the box model. Before coming into the "span.greypen" declaration, the browser already assigned values for these properties using the more generic "span" selector. So unless you reassign new values to those properties, the previous rules cascading down to the "greypen" span will stay in force.

The Firebug screenshot shows what I have described above about "greypen" selector graphically. It only has one declaration which is for the color so it writes over the color declaration coming from the more general "span" selector. The color codes being the same, we don't see any difference but that's Ok. More importantly, it's how "greypen" unwittingly acquires a relative position and an offset of minus 25 pixels all due to the cascade. See how those "old rules" are not crossed out and still show up in the Firebug output.


How do you bypass this? It's very simple, you need to write over it the old fashioned way. You will declare your own position in the new selector and since we want to stay exactly where we are, we can simply reset the position back to the normal flow using "static" which is the default value for the position property.

/* ---------------------------------------------------------------------------------- */
.bargraph ul.bars li span.greypen {
position: static;
color: #7c7c7c;
}


The new rule resets the position set by the generic span and prints the number inside the li element in the new dark color instead of white. Two spans, two very different results.

Post a Comment