CSS’ barrier to entry is extremely low, mainly due to the nature of its syntax. Being clear and easy to understand, the syntax makes sense even to the inexperienced Web designer. It’s so simple, in fact, that you could style a simple CSS-based website within a few hours of learning it.
But this apparent simplicity is deceitful. If after a few hours of work, your perfectly crafted website looks great in Safari, all hell might break loose if you haven’t taken the necessary measures to make it work in Internet Explorer. In a panic, you add hacks and filters where only a few tweaks or a different approach might do. Knowing how to deal with these issues comes with experience, with trial and error and with failing massively and then learning the correct way.
Understanding a few often overlooked concepts is also important. The concepts may be hard to grasp and look boring at first, but understanding them and knowing how to take advantage of them is important.
Two of these concepts are specificity and inheritance. Not very common words among Web designers, are they? Talking about
text-shadow is a lot more fun; but specificity and inheritance are fundamental concepts that any person who wants to be good at CSS should understand. They will help you create clean, maintainable and flexible style sheets. Let’s look at what they mean and how they work.
The notion of a “cascade” is at the heart of CSS (just look at its name). It ultimately determines which properties will modify a given element. The cascade is tied to three main concepts: importance, specificity and source order. The cascade follows these three steps to determine which properties to assign to an element. By the end of this process, the cascade has assigned a weight to each rule, and this weight determines which rule takes precedence, when more than one applies.
Please consider reading our previous related article:
[By the way: The network tab (on the top of the page) is updated several times a day. It features manually selected articles from the best web design blogs!]
Style sheets can have a few different sources:
- User agent
For example, the browser’s default style sheet.
Such as the user’s browser options.
This is the CSS provided by the page (whether inline, embedded or external)
By default, this is the order in which the different sources are processed, so the author’s rules will override those of the user and user agent, and so on.
There is also the
!important declaration to consider in the cascade. This declaration is used to balance the relative priority of user and author style sheets. While author style sheets take precedence over user ones, if a user rule has
!important applied to it, it will override even an author rule that also has
!important applied to it.
Knowing this, let’s look at the final order, in ascending order of importance:
- User agent declarations,
- User declarations,
- Author declarations,
This flexibility in priority is key because it allows users to override styles that could hamper the accessibility of a website. (A user might want a larger font or a different color, for example.)
Every CSS rule has a particular weight (as mentioned in the introduction), meaning it could be more or less important than the others or equally important. This weight defines which properties will be applied to an element when there are conflicting rules.
Upon assessing a rule’s importance, the cascade attributes a specificity to it; if one rule is more specific than another, it overrides it.
If two rules share the same weight, source and specificity, the later one is applied.
2.1 How to Calculate Specificity?
There are several ways to calculate a selector’s specificity.
The quickest way is to do the following. Add 1 for each element and pseudo-element (for example,
:after); add 10 for each attribute (for example,
[type=”text”]), class and pseudo-class (for example,
:hover); add 100 for each ID; and add 1000 for an inline style.
Let’s calculate the specificity of the following selectors using this method:
1 class + 1 element = 11
1 ID + 1 attribute + 1 element = 111
body #main .post ul li:last-child
1 ID + 1 class + 1 pseudo-class + 3 elements = 123
A similar method, described in the W3C’s specifications, is to start with a=0, b=0, c=0 and d=0 and replace the numbers accordingly:
- a = 1 if the style is inline,
- b = the number of IDs,
- c = the number of attribute selectors, classes and pseudo-classes,
- d = the number of element names and pseudo-elements.
Let’s calculate the specificity of another set of selectors:
p style="color: rgb(0, 0, 0);"
a=1, b=0, c=0, d=0 → 1000
footer nav li:last-child
a=0, b=0, c=1, d=3 → 0013
a=0, b=1, c=1, d=1 → 0111
(Note that the negation pseudo-class doesn’t count, but the selector inside it does.)
If you’d rather learn this in a more fun way, Andy Clarke drew a clever analogy between specificity and Star Wars back in 2005, which certainly made it easier for Star Wars fans to understand specificity. Another good explanation is “CSS Specificity for Poker Players,” though slightly more complicated.
Andy Clarke’s CSS Specificity Wars chart.
Remember that non-CSS presentational markup is attributed with a specificity of 0, which would apply, for example, to the
Getting back to the
!important declaration, keep in mind that using it on a shorthand property is the same as declaring all of its sub-properties as
!important (even if that would revert them to the default values).
If you are using imported style sheets (
@import) in your CSS, you have to declare them before all other rules. Thus, they would be considered as coming before all the other rules in the CSS file.
Finally, if two selectors turn out to have the same specificity, the last one will override the previous one(s).
2.2 Making Specificity Work For You
If not carefully considered, specificity can come back to haunt you and lead you to unwittingly transform your style sheets into a complex hierarchy of unnecessarily complicated rules.
You can follow a few guidelines to avoid major issues:
- When starting work on the CSS, use generic selectors, and add specificity as you go along;
- Using advanced selectors doesn’t mean using unnecessarily complicated ones;
- Rely more on specificity than on the order of selectors, so that your style sheets are easier to edit and maintain (especially by others).
A good rule of thumb can be found in Jim Jeffers’ article, “The Art and Zen of Writing CSS”:
Refactoring CSS selectors to be less specific is exponentially more difficult than simply adding specific rules as situations arise.
A succinct and clear explanation of inheritance is in the CSS3 Cascading and Inheritance module specifications (still in “Working draft” mode):
Inheritance is a way of propagating property values from parent elements to their children.
Some CSS properties are inherited by the children of elements by default. For example, if you set the
body tag of a page to a specific font, that font will be inherited by other elements, such as headings and paragraphs, without you having to specifically write as much. This is the magic of inheritance at work.
The CSS specification determines whether each property is inherited by default or not. Not all properties are inherited, but you can force ones to be by using the
3.1 Object-Oriented Programming Inheritance
Though beyond the scope of this article, CSS inheritance shouldn’t be confused with object-oriented programming (OOP) inheritance. Here is the definition of OOP inheritance from Wikipedia, and it makes clear that we are not talking about the same thing:
In object-oriented programming (OOP), inheritance is a way to form new classes […] using classes that have already been defined. Inheritance is employed to help reuse existing code with little or no modification. The new classes […] inherit attributes and behavior of the pre-existing classes. …
3.2 How Inheritance Works
When an element inherits a value from its parent, it is inheriting its computed value. What does this mean? Every CSS property goes through a four-step process when its value is being determined. Here’s an excerpt from the W3C specification:
The final value of a property is the result of a four-step calculation: the value is determined through specification (the “specified value”), then resolved into a value that is used for inheritance (the “computed value”), then converted into an absolute value if necessary (the “used value”), and finally transformed according to the limitations of the local environment (the “actual value”).
In other words:
- Specified value
The user agent determines whether the value of the property comes from a style sheet, is inherited or should take its initial value.
- Computed value
The specified value is resolved to a computed value and exists even when a property doesn’t apply. The document doesn’t have to be laid out for the computed value to be determined.
- Used value
The used value takes the computed value and resolves any dependencies that can only be calculated after the document has been laid out (like percentages).
- Actual value
This is the value used for the final rendering, after any approximations have been applied (for example, converting a decimal to an integer).
If you look at any CSS property’s specification, you will see that it defines its initial (or default) value, the elements it applies to, its inheritance status and its computed value (among others). For example, the
background-color specification states the following:
Applies to: all elements
Computed value: the computed color(s)
Confusing? It can be. So, what do we need to understand from all this? And why is it relevant to inheritance?
Let’s go back to the first sentence of this section, which should make more sense now. When an element inherits a value from its parent, it inherits its computed value. Because the computed value exists even if it isn’t specified in the style sheet, a property can be inherited even then: the initial value will be used. So, you can make use of inheritance even if the parent doesn’t have a specified property.
3.3 Using Inheritance
The most important thing to know about inheritance is that it’s there and how it works. If you ignore the jargon, inheritance is actually very straightforward.
Imagine you had to specify the
font-family of every element, instead of simply adding it to the
body element? That would cumbersome, which is why inheritance is so helpful.
Don’t break it by using the universal selector (
*) with properties that inherit by default. Bobby Jack wrote an interesting post about this on his Five-Minute Argument blog. You don’t have to remember all of the properties that inherit, but you will in time.
Rarely does a CSS-related article not bring some kind of bad news about Internet Explorer. This article is no exception. IE supports the
inherit value only from version 8, except for the
visibility properties. Great.
4. Using Your Tools
If you use tools like Firebug or Safari’s Web Inspector, you can see how a given cascade works, which selectors have higher specificity and how inheritance is working on a particular element.
For example, here below is Firebug in action, inspecting an element on the page. You can see that some properties are overridden (i.e. crossed out) by other more specific rules:
Firebug in action, informing you how specificity is working.
In the next shot, Safari’s Web Inspector shows the computed values of an element. This way, you can see the values even though they haven’t been explicitly added to the style sheet:
With Safari’s Web Inspector (and Firebug), you can view the computed values of a particular element.
Hopefully this article has opened your eyes to (or has refreshed your knowledge of) CSS inheritance and specificity.
Even if you don’t think about them, these issues are present in your daily work as a CSS author. Especially in the case of specificity, it’s important to know how they affect your style sheets and how to plan for them so that they cause only minimal (or no) problems.
from Smashing Magazine