Read CSS: The Definitive Guide, 3rd Edition Online
Authors: Eric A. Meyer
Tags: #COMPUTERS / Web / Page Design
As important as specificity may be to understanding how
declarations are applied to a document, another key concept is inheritance.
Inheritance is the mechanism by which
styles are applied not only to a specified element, but also to its descendants. If a
color is applied to anh1
element, for example, then
that color is applied to all text in theh1
, even the
text enclosed within child elements of thath1
:
h1 {color: gray;}
Meerkat Central
Both the ordinaryh1
text and theem
text are colored gray because theem
element inherits the value ofcolor
. If property values could not be inherited by descendant elements,
theem
text would be black, not gray, and you'd have
to color that element separately.
Inheritance also works well with unordered lists. Let's say you apply a style ofcolor
:gray;
forul
elements:
ul {color: gray;}
You expect that a style that is applied to aul
will also be applied to its list items, and to any content of those list items. Thanks
to inheritance, that's exactly what happens, as
Figure 3-3
demonstrates.
Figure 3-3. Inheritance of styles
It's easier to see how inheritance works by turning to a tree diagram of a document.
Figure 3-4
shows the tree diagram for a
very simple document containing two lists: one unordered and the other ordered.
Figure 3-4. A simple tree diagram
When the declarationcolor
:gray;
is applied to theul
element, that element takes on that declaration. The value is then
propagated down the tree to the descendant elements and continues on until there are no
more descendants to inherit the value. Values are
never
propagated
upward; that is, an element never passes values up to its ancestors.
There is an exception to the upward propagation rule in HTML: background styles
applied to thebody
element can be passed to thehtml
element, which is the document's root
element and therefore defines its canvas.
Inheritance is one of those things about CSS that is so basic that you almost never
think about it unless you have to. However, you should still keep a few things in mind.
First, note that some properties are not inherited—generally as a result of simple
common sense. For example, the propertyborder
(which
is used to set borders on elements) does not inherit. A quick glance at
Figure 3-5
reveals why this is the case. If
borders were inherited, documents would become much more cluttered—unless the author
took the extra effort to turn off the inherited borders.
Figure 3-5. Why borders aren't inherited
As it happens, most of the box-model properties—including margins, padding,
backgrounds, and borders—are not inherited for the same reason. After all, you wouldn't
want all of the links in a paragraph to inherit a 30-pixel left margin from their parent
element!
Inherited values have no specificity at all, not even zero specificity. This seems
like an academic distinction until you work through the consequences of the lack of
inherited specificity. Consider the following rules and markup fragment and compare them
to the result shown in
Figure 3-6
:
* {color: gray;}
h1#page-title {color: black;}
Meerkat Central
Welcome to the best place on the web for meerkat information!
Figure 3-6. Zero specificity defeats no specificity
Inherit the Bugs
Due to problems in various browser implementations, an author cannot rely on
inheritance to operate as expected in all circumstances. For example, Navigator 4
(and, to a lesser extent, Explorer 4 and 5) does not inherit styles into tables.
Thus, the following rule would result in a document with smaller text everywhere
outside of tables:
body {font-size: 0.8em;}
This is not correct behavior under CSS, but it does exist, so authors have
historically resorted to tricks such as:
body, table, th, td {font-size: 0.8em;}
This is more likely, although still not guaranteed, to achieve the desired effect
in buggy browsers.
Unfortunately, the above "fix" leads to an even worse problem in browsers that do
implement inheritance correctly, such as IE6/Win, IE5/Mac, Netscape 6+, and others.
In those browsers, you will end up with text inside a table cell that is 41 percent
of the size of the user's default font size setting. It is often more dangerous to
attempt to work around inheritance bugs in old browsers than it is to write correct
CSS for updated browsers.
Since the universal selector applies to all elements and has zero specificity, its
color declaration's value ofgray
wins out over the
inherited value ofblack
, which has no specificity at
all. Therefore, theem
element is rendered gray
instead of black.
This example vividly illustrates one of the potential problems of using the universal
selector indiscriminately. Because it can match any element, the universal selector
often has the effect of short-circuiting inheritance. This can be worked around, but
it's usually more sensible to avoid the problem in the first place by not using the
universal selector indiscriminately.
The complete lack of specificity for inherited values is not a trivial point. For
example, assume that a style sheet has been written such that all text in a "toolbar" is
to be white on black:
#toolbar {color: white; background: black;}
This will work as long as the element with anid
oftoolbar
contains nothing but plain text. If,
however, the text within this element is all hyperlinks (a
elements), then the user agent's styles for hyperlinks will take over. In a
web browser, this means they'll likely be colored blue, since the browser's style sheet
probably contains an entry like this:
a:link {color: blue;}
To overcome this problem, you must declare:
#toolbar {color: white; background: black;}
#toolbar a:link {color: white;}
By targeting the rule directly to thea
elements
within the toolbar, you'll get the result shown in
Figure 3-7
.
Figure 3-7. Directly assigning styles to the relevant elements
Throughout this chapter, we've skirted one rather
important issue: what happens when two rules of equal specificity apply to the same
element? How does the browser resolve the conflict? For example, say you have the
following rules:
h1 {color: red;}
h1 {color: blue;}
Which one wins? Both have a specificity of0,0,0,1
, so they have equal weight and should both apply. That simply can't
be the case because the element can't be both red and blue. But which will it be?
Finally, the name "Cascading Style Sheets" makes some sense. CSS is based on a method
of causing styles to cascade together, which is made possible by combining inheritance
and specificity. The cascade rules
for CSS2.1 are simple enough:
Find all rules that contain a selector that matches a given element.
Sort by explicit weight all declarations applying to the element. Those rules
marked!important
are given higher weight than
those that are not. Sort by origin all declarations applying to a given element.
There are three origins: author, reader, and user agent. Under normal
circumstances, the author's styles win out over the reader's styles.!important
reader styles are stronger than any other
styles, including!important
author styles.
Both author and reader styles override the user agent's default styles.
Sort by specificity all declarations applying to a given element. Those
elements with a higher specificity have more weight than those with lower
specificity.
Sort by order all declarations applying to a given element. The later a
declaration appears in the style sheet or document, the more weight it is given.
Declarations that appear in an imported style sheet are considered to come before
all declarations within the style sheet that imports them.
To be perfectly clear about how this all works, let's consider three examples that
illustrate the last three of the four cascade rules.
Under the second rule, if two rules
apply to an element, and one is marked!important
,
the important rule wins out:
p {color: gray !important;}Well, hello there!
Despite the fact that there is a color assigned in thestyle
attribute of the paragraph, the!important
rule wins out, and the paragraph is gray. This gray is
inherited by theem
element as well.
Furthermore, the origin of
a rule is considered. If an element is matched by
normal-weight styles in both the author's style sheet and the reader's style sheet,
then the author's styles are used. For example, assume that the following styles come
from the indicated origins:
p em {color: black;} /* author's style sheet */
p em {color: yellow;} /* reader's style sheet */
In this case, emphasized text within paragraphs is colored black, not yellow,
because normal-weight author styles win out over normal-weight reader styles.
However, if both rules are marked!important
, the
situation changes:
p em {color: black !important;} /* author's style sheet */
p em {color: yellow !important;} /* reader's style sheet */
Now the emphasized text in paragraphs will be yellow, not black.
As it happens, the user agent's default styles—which are often influenced by the
user preferences—are figured into this step. The default style declarations are the
least influential of all. Therefore, if an author-defined rule applies to anchors
(e.g., declaring them to bewhite
), then this rule
overrides the user agent's defaults.
To sum up, there are five levels to consider in terms of declaration weight. In
order of most to least weight, these are:
Reader important declarations
Author important declarations
Author normal declarations
Reader normal declarations
User agent declarations
Authors typically need to worry about only the first four weight levels, since
anything declared will win out over the user agent styles.
According to the third rule, if conflicting
declarations apply to an element and they all have the same weight, they should be
sorted by specificity, with the most specific declaration winning out. For example:
p#bright {color: silver;}
p {color: black;}Well, hello there!
Given the rules shown, the text of the paragraph will be silver, as illustrated in
Figure 3-8
. Why? Because the
specificity ofp#bright (0,1,0,1)
overrode the
specificity ofp (0,0,0,1)
, even though the latter
rule comes later in the style sheet.
Figure 3-8. Higher specificity wins out over lower specificity
Finally, under the fourth rule, if two rules
have exactly the same weight, origin, and specificity, then the one that occurs later
in the style sheet wins out. Therefore, let's return to our earlier example, where we
find the following two rules in the document's style sheet:
h1 {color: red;}
h1 {color: blue;}
Because its rule comes later in the style sheet, the value ofcolor
for allh1
elements in the document will beblue
, notred
. Any rule that is contained in the document
with a higher weight than the imported rule wins out. This is true even if the rule
is part of the document's style sheet and not part of an element'sstyle
attribute. Consider the following:
p em {color: purple;} /* from imported style sheet */
p em {color: gray;} /* rule contained within the document */
In this case, the second rule shown will win out over the imported rule because it
is a part of the document's style sheet.
For the purposes of this rule, styles specified in thestyle
attribute of an element are considered to be at the end of the
document's style sheet, which places them after all other rules. However, this is a
largely academic point, since inline
style declarations have a
higher specificity than any style sheet selector in CSS2.1.
Remember that in CSS2, inline style declarations have a specificity equal to ID
selectors. In a CSS2 (but not CSS2.1) user agent,style
attribute declarations are considered to appear at the end of the
document's style sheet and are sorted by weight, origin, specificity, and order as
with any other declaration in the style sheet.
Order sorting is the reason behind the often-recommended ordering of link styles.
The recommendation is that you array your link styles in the order
link-visited-hover-active, or LVHA, like this:
:link {color: blue;}
:visited {color: purple;}
:hover {color: red;}
:active {color: orange;}
Thanks to the information in this chapter, you now know that the specificity of
all of these selectors is the same:0,0,1,0
.
Because they all have the same weight, origin, and specificity, the last one that
matches an element will win out. An unvisited link that is being "clicked" is matched
by three of the rules—:link
,:hover
, and:active
—so the last one of those three declared will win out. Given the
LVHA ordering,:active
will win, which is likely
what the author intended.
Assume for a moment that you decide to ignore the common ordering and alphabetize
your link styles instead. This would yield:
:active {color: orange;}
:hover {color: red;}
:link {color: blue;}
:visited {color: purple;}
Given this ordering, no link would ever show:hover
or:active
styles because the:link
and:visited
rules come after the other two. Every link must be either visited
or unvisited, so those styles will always override the:hover
rule.
Let's consider a variation on the LVHA order that an author might want to use. In
this ordering, only unvisited links will get a hover style; visited links do not.
Both visited and unvisited links will get an active style:
:link {color: blue;}
:hover {color: red;}
:visited {color: purple;}
:active {color: orange;}
Of course, sometimes such conflicts arise when all the states attempt to set the
same property. If each state styles a different property, then the order does not
matter. In the following case, the link styles could be given in any order and would
still function:
:link {font-weight: bold;}
:visited {font-style: italic;}
:hover {color: red;}
:active {background: yellow;}
You may also have realized that the order of the:link
and:visited
styles doesn't
matter. You could order the styles LVHA or VLHA with no ill effect. However, LVHA
tends to be preferred because it was recommended in the CSS2 specification and also
because the mnemonic "LoVe—HA!" gained rather wide currency. (There's some bitterness
out there, apparently.)
The ability to chain pseudo-classes together eliminates these worries. The
following could be listed in any order without any negative effects:
:link {color: blue;}
:visited {color: purple;}
:link:hover {color: red;}
:visited:hover {color: gray;}
Because each rule applies to a unique set of link states, they do not conflict.
Therefore, changing their order will not change the styling of the document. The last
two rules do have the same specificity, but that doesn't matter. A hovered unvisited
link will not be matched by the rule regarding hovered visited links, and vice versa.
If we were to add active-state styles, then order would start to matter again.
Consider:
:link {color: blue;}
:visited {color: purple;}
:link:hover {color: red;}
:visited:hover {color: gray;}
:link:active {color: orange;}
:visited:active {color: silver;}
If the active styles were moved before the hover styles, they would be ignored.
Again, this would happen due to specificity conflicts. The conflicts could be avoided
by adding more pseudo-classes to the chains, like this:
:link:hover:active {color: orange;}
:visited:hover:active {color: silver;}
Chained pseudo-classes, which lessen worries about specificity and ordering, would
be used much more often if Internet Explorer had historically supported them. (See
Chapter 2
for more information on this
subject.)
It is possible that a document will contain presentational
hints that are not CSS—e.g., thefont
element.
Non-CSS hints are treated as if they have a specificity of0
and appear at the
beginning
of the author's style
sheet. Such presentation hints will be overridden by any author or reader styles, but
not by the user agent's styles.