Saturday, December 04, 2010

CSS Inheritance and Cascading



When you are first starting out with CSS, you may have some confusion about the concepts of inheritance and cascading, as well as specificity. Let's take a look at these ideas and see if we can make them clearer. We'll start with inheritance.

Inheritance


Let's say your HTML looks like this:
<body>
  <div>
     <h1>The Title</ht>
     <p>
         Some text.
     </p>
  </div>
</body>

That results in a document tree that looks like this:
body
    |-div
        |-h1
        |-p
If you add more elements the tree will become more complex, but even this simple document can demonstrate what CSS inheritance is all about. Suppose you have the following CSS:

body {
color: green;
}

This will cause both the h1 heading and the text of the paragraph to be rendered in green. The reason is that the div inherits the color green from it's parent, the body element. Then the h1 and p inherit the color from their parent, the div. This is just like a trait such as eye color being passed down from one generation of people to the next.

Now suppose we add something else to the CSS:

body {
color: green;
border: 1px solid red;
}

What do you think will happen here?
Will everything be green with a red border?

No it won't because the border property is defined by the CSS standard to not be inheritable. For each CSS property the W3C CSS specification tells you whether it is or isn't inheritable. So think of things like borders as being like tattoos: they can change the appearance of the parent, but they aren't inherited to the next generation.

Now consider what would happen if we add the following CSS rule:

p {
color: navy;
}

The heading will still be green, but now the paragraph text will be navy. Furthermore, if there were more elements underneath the p, then they would all inherit 'navy' from their p parent.

The key idea here is that the value of an inheritable property p, for element x, are taken from the parent element *unless* some CSS rule - any CSS rule - has something to say about that property's value. So any applicable CSS rule, will override inheritance from the parent. Think of CSS rules like Cosmic Rays that come in and modify the genes of a child so that her eyes are brown instead of blue like the parent. Inheritance doesn't matter when a CSS rule makes a specific change to a property.

So far, so good. Now let's talk about Cascading.

Cascading


For cascading, try to forget all about the structure of the HTML document and the parent-child relationships we talked about above. Cascading has more to do with the many CSS rules that can apply to an individual HTML element, and deciding which rules really apply to the element's appearance. An analogy is a persons education level, like "High School Graduate", "Bachelor Degree", "Masters Degree", "PhD". It has nothing to do with inheritance, and is instead an attribute of the individual.

To carry the analogy a little further, imagine that 100 people from the town of Smallville all graduated from Smallville High School together. 50 of those 100 went on to get Bachelor degrees at Bigtown U. Now all 100 are technically high school graduates. But we commonly refer to the ones who finished college as "college graduates", and the ones who only finished high school as "high school graduates".

Now imagine that we attach properties to each person, where the properties are "home-town" and "education-level". For 50 people the values will be:

home-town: smallville
education-level: high-school-graduate

and the other 50 will be:

home-town: smallville
education-level: college-graduate

We can derive these values from a couple of simple rules:

1. All 100 people are from Smallville and graduated from Smallville High School.

2. 50 people graduate from Bigtown U.

A very similar process goes on in the Browser as it's trying to figure out all the CSS properties that apply to a given element. Here's how to think about it:

First, every given element on the page there is a set of properties (just like home-town, and education-level).

These properties will have default values set by the Browser.

The default values are over-written by values inherited from parents.

The inherited values are over-written by values in applicable CSS rules. This is where the cascade comes into play. The cascade works like this:

a. Find all CSS rules where the selector applies to this element.
b. Sort those CSS rules by specificity, with low specificity rules at the top of the list, and high specificity at the bottom.
c. For those rules where specificity is the same, put rules that appear later in the code closer to the bottom of the list.
d. Now starting at the top of the list, when you see a property value, write it down. If you see that same property again further down the list, cross out the old value and use the new one. So the values found further down the list take precedence.

So, for example, let's say you have these five rules, and we're working out the property values for the paragraph in the example markup given earlier:

p { color: pink; font-size: 10px; }
body p { color: green; font-size: 20px;}
body div p { color: yellow; }
body div p { color: red; }
body div { color: blue; }

After we choose only those rules that apply to the p tag, and sort by specificity (and secondarily by order of occurrence), the list looks like this:

(Less specific)
p { color: pink; font-size: 10px; }
body p { color: green; font-size: 20px; }
body div p { color: yellow; }
body div p { color: red; }
(More specific)

Note that we don't include the rule for "body div" because that selector does not target the p element that we are working on. Now to determine the property values that will apply to the p element, we walk down the list looking for property values. We find color: pink right away and write that down. As we keep going we find green, then yellow, then red. In each case we replace the value with the one further down the list, so we end up with color: red.

We do the same thing with font-size. First we see font-size: 10px, and write it down. Then we find font-size: 20px, cross out the 10px and write down 20px. So our final properties that are set via the CSS cascade are:

color: red;
font-size: 20px;

This whole process of finding the set of applicable CSS rules, and then determining how they affect the properties of the element, is what Cascading is all about. Look again at the sorted list of CSS rules and notice how the font-size "cascades" down through multiple rules, even though those rules are more specific.

To summarize, the key idea of Cascading is looking at all the CSS rules that apply to a given HTML element, and determining which property values to pull from those rules and apply to the element.

And finally, remember that any properties of the element that are not affected directly by the CSS cascade can end up being set by inheritance, or by Browser default values, or even by Browser runtime calculations (e.g. width and height).

Post a Comment