This is a paragraph with an inline element within it.
Here we have two block elements (body and p) and an inline element (em). According
to the HTML specification, em can descend from p, but the reverse is not true. Typi‐
cally, the HTML hierarchy works out so that inlines descend from blocks, but not the
other way around.
CSS, on the other hand, has no such restrictions. You can leave the markup as it is but
change the display roles of the two elements like this:
p {display: inline;}
em {display: block;}
Elements
|
5
This causes the elements to generate a block box inside an inline box. This is perfectly
legal and violates no part of CSS. You would, however, have a problem if you tried to
reverse the nesting of the elements in HTML:
This is a paragraph improperly enclosed by an inline element.
No matter what you do to the display roles via CSS, this is not legal in HTML.
While changing the display roles of elements can be useful in HTML documents, it
becomes downright critical for XML documents. An XML document is unlikely to
have any inherent display roles, so it’s up to the author to define them. For example,
you might wonder how to lay out the following snippet of XML:
Cascading Style Sheets: The Definitive GuideThird EditionEric A. MeyerO'Reilly and AssociatesNovember 2006978-0-596-52733-4CSS Pocket ReferenceThird EditionEric A. MeyerO'Reilly and AssociatesOctober 2007978-0-596-51505-8
Since the default value of display is inline, the content would be rendered as inline
text by default, as illustrated in Figure 1-2. This isn’t a terribly useful display.
Figure 1-2. Default display of an XML document
You can define the basics of the layout with display:
book, maintitle, subtitle, author, isbn {display: block;}
publisher, pubdate {display: inline;}
We’ve now set five of the seven elements to be block and two to be inline. This means
each of the block elements will be treated much as div is treated in HTML, and the
two inlines will be treated in a manner similar to span.
This fundamental ability to affect display roles makes CSS highly useful in a variety of
situations. We could take the preceding rules as a starting point, add a few other styles
for greater visual impact, and get the result shown in Figure 1-3.
6
|
Chapter 1: CSS and Documents
Figure 1-3. Styled display of an XML document
Before learning how to write CSS in detail, we need to look at how one can associate
CSS with a document. After all, without tying the two together, there’s no way for the
CSS to affect the document. We’ll explore this in an HTML setting since it’s the most
familiar.
Bringing CSS and HTML Together
I’ve mentioned that HTML documents have an inherent structure, and that’s a point
worth repeating. In fact, that’s part of the problem with web pages of old: too many of
us forgot that documents are supposed to have an internal structure, which is alto‐
gether different than a visual structure. In our rush to create the coolest-looking
pages on the web, we bent, warped, and generally ignored the idea that pages should
contain information with some structural meaning.
That structure is an inherent part of the relationship between HTML and CSS;
without it, there couldn’t be a relationship at all. To understand it better, let’s look at
an example HTML document and break it down by pieces:
Eric's World of Waffles
Waffles!
The most wonderful of all breakfast foods is
the waffle—a ridged and cratered slab of home-cooked, fluffy goodness
Bringing CSS and HTML Together
|
7
that makes every child's heart soar with joy. And they're so easy to make!
Just a simple waffle-maker and some batter, and you're ready for a morning
of aromatic ecstasy!
The result of this markup and the applied styles is shown in Figure 1-4.
Figure 1-4. A simple document
Now, let’s examine the various ways this document connects to CSS.
The link Tag
First, consider the use of the link tag:
The link tag is a little-regarded but nonetheless perfectly valid tag that has been
hanging around the HTML specification for years, just waiting to be put to good use.
Its basic purpose is to allow HTML authors to associate other documents with the
document containing the link tag. CSS uses it to link stylesheets to the document; in
Figure 1-5, a stylesheet called sheet1.css is linked to the document.
These stylesheets, which are not part of the HTML document but are still used by it,
are referred to as external stylesheets. This is because they’re stylesheets that are exter‐
nal to the HTML document. (Go figure.)
To successfully load an external stylesheet, link must be placed inside the head ele‐
ment but may not be placed inside any other element. This will cause the web
browser to locate and load the stylesheet and use whatever styles it contains to render
the HTML document in the manner shown in Figure 1-5. Also shown in Figure 1-5 is
the loading of the external sheet2.css via the @import declaration. Imports must be
placed at the beginning of the stylesheet that contains them, but they are otherwise
unconstrained.
8
|
Chapter 1: CSS and Documents
Figure 1-5. A representation of how external stylesheets are applied to documents
And what is the format of an external stylesheet? It’s a list of rules, just like those we
saw in the previous section and in the example HTML document; but in this case, the
rules are saved into their own file. Just remember that no HTML or any other markup
language can be included in the stylesheet—only style rules. Here are the contents of
an external stylesheet:
h1 {color: red;}
h2 {color: maroon; background: white;}
h3 {color: white; background: black;
font: medium Helvetica;}
That’s all there is to it—no HTML markup or comments at all, just plain-and-simple
style declarations. These are saved into a plain-text file and are usually given an
extension of .css, as in sheet1.css.
An external stylesheet cannot contain any document markup at all,
only CSS rules and CSS comments, both of which are explained
later in the chapter. The presence of markup in an external style‐
sheet can cause some or all of it to be ignored.
The filename extension is not required, but some older browsers won’t recognize the
file as containing a stylesheet unless it actually ends with .css, even if you do include
the correct type of text/css in the link element. In fact, some web servers won’t
hand over a file as text/css unless its filename ends with .css, though that can usually
be fixed by changing the server’s configuration files.
Bringing CSS and HTML Together
|
9
Attributes
For the rest of the link tag, the attributes and values are fairly straightforward. rel
stands for “relation,” and in this case, the relation is stylesheet. The attribute type is
always set to text/css. This value describes the type of data that will be loaded using
the link tag. That way, the web browser knows that the stylesheet is a CSS stylesheet,
a fact that will determine how the browser deals with the data it imports. After all,
there may be other style languages used in the future, so it’s important to declare
which language you’re using.
Next, we find the href attribute. The value of this attribute is the URL of your style‐
sheet. This URL can be either absolute or relative, depending on what works for you.
In our example, the URL is relative. It just as easily could have been something like
http://meyerweb.com/sheet1.css.
Finally, we have a media attribute. The value of this attribute is one or more media
descriptors, which are rules regarding media types and the features of those media,
with each rule separated by a comma. Thus, for example, you can use a linked style‐
sheet in both screen and projection media:
Media descriptors can get quite complicated, and are explained in detail later in the
chapter. For now, we’ll stick with the basic media types shown.
Note that there can be more than one linked stylesheet associated with a document.
In these cases, only those link tags with a rel of stylesheet will be used in the ini‐
tial display of the document. Thus, if you wanted to link two stylesheets named
basic.css and splash.css, it would look like this:
This will cause the browser to load both stylesheets, combine the rules from each, and
apply them all to the document. For example:
This paragraph will be gray only if styles from the
stylesheet 'basic.css' are applied.
This paragraph will be gray only if styles from the
stylesheet 'splash.css' are applied.
The one attribute that is not in this example markup, but could be, is the title
attribute. This attribute is not often used, but it could become important in the future
and, if used improperly, can have unexpected effects. Why? We will explore that in
the next section.
10
| Chapter 1: CSS and Documents
Alternate stylesheets
It’s also possible to define alternate stylesheets. These are defined by making the value
of the rel attribute alternate stylesheet, and they are used in document presenta‐
tion only if selected by the user.
Should a browser be able to use alternate stylesheets, it will use the values of the link
element’s title attributes to generate a list of style alternatives. So you could write
the following:
Users could then pick the style they want to use, and the browser would switch from
the first one, labeled “Default” in this case, to whichever the user picked. Figure 1-6
shows one way in which this selection mechanism might be accomplished (and in
fact was, early in the resurgence of CSS).
Figure 1-6. A browser offering alternate stylesheet selection
As of late 2016, alternate stylesheets were supported in most
Gecko-based browsers like Firefox, and in Opera. They could be
supported in the Internet Explorer family through the use of Java‐
Script but are not natively supported by those browsers. The Web‐
Kit family did not support selecting alternate stylesheets. Compare
this to the age of the browser shown in Figure 1-6--it’s almost
shocking.
It is also possible to group alternate stylesheets together by giving them the same
title value. Thus, you make it possible for the user to pick a different presentation
for your site in both screen and print media:
Bringing CSS and HTML Together
|
11
If a user selects “Big Text” from the alternate stylesheet selection mechanism in a con‐
forming user agent, then bigtext.css will be used to style the document in the screen
medium, and print-bigtext.css will be used in the print medium. Neither sheet1.css nor
print-sheet1.css will be used in any medium.
Why is that? Because if you give a link with a rel of stylesheet a title, then you are
designating that stylesheet as a preferred stylesheet. This means that its use is preferred
to alternate stylesheets, and it will be used when the document is first displayed. Once
you select an alternate stylesheet, however, the preferred stylesheet will not be used.
Furthermore, if you designate a number of stylesheets as preferred, then all but one of
them will be ignored. Consider the following code example:
All three link elements now refer to preferred stylesheets, thanks to the presence of a
title attribute on all three, but only one of them will actually be used in that manner.
The other two will be ignored completely. Which two? There’s no way to be certain, as
HTML doesn’t provide a method of determining which preferred stylesheets should
be ignored and which should be used.
If you don’t give a stylesheet a title, then it becomes a persistent stylesheet and is
always used in the display of the document. Often, this is exactly what an author
wants.
The style Element
The style element is one way to include a stylesheet, and it appears in the document
itself:
style should always use the attribute type; in the case of a CSS document, the correct
value is "text/css", just as it was with the link element.
12
|
Chapter 1: CSS and Documents
The style element should always start with tag. It is also possible to give the style element a media attribute,
which functions in the same manner as previously discussed for linked stylesheets.
The styles between the opening and closing style tags are referred to as the docu‐
ment stylesheet or the embedded stylesheet (because this kind of stylesheet is embed‐
ded within the document). It will contain many of the styles that will apply to the
document, but it can also contain multiple links to external stylesheets using the
@import directive.
The @import Directive
Now we’ll discuss the stuff that is found inside the style tag. First, we have some‐
thing very similar to link: the @import directive:
@import url(sheet2.css);
Just like link, @import can be used to direct the web browser to load an external
stylesheet and use its styles in the rendering of the HTML document. The only major
difference is in the syntax and placement of the command. As you can see, @import is
found inside the style container. It must be placed before the other CSS rules or it
won’t work at all. Consider this example:
Like link, there can be more than one @import statement in a document. Unlike
link, however, the stylesheets of every @import directive will be loaded and used;
there is no way to designate alternate stylesheets with @import. So, given the follow‐
ing markup:
@import url(sheet2.css);
@import url(blueworld.css);
@import url(zany.css);
all three external stylesheets will be loaded, and all of their style rules will be used in
the display of the document.
As with link, you can restrict imported stylesheets to one or more media by provid‐
ing media descriptors after the stylesheet’s URL:
@import url(sheet2.css) all;
@import url(blueworld.css) screen;
@import url(zany.css) projection, print;
Bringing CSS and HTML Together
|
13
As noted in “The link Tag” on page 8, media descriptors can get quite complicated,
and are explained in detail in Chapter 20, Media-Dependent Styles.
@import can be highly useful if you have an external stylesheet that needs to use the
styles found in other external stylesheets. Since external stylesheets cannot contain
any document markup, the link element can’t be used—but @import can. Therefore,
you might have an external stylesheet that contains the following:
@import url(http://example.org/library/layout.css);
@import url(basic-text.css);
@import url(printer.css) print;
body {color: red;}
h1 {color: blue;}
Well, maybe not those exact styles, but hopefully you get the idea. Note the use of
both absolute and relative URLs in the previous example. Either URL form can be
used, just as with link.
Note also that the @import directives appear at the beginning of the stylesheet, as they
did in the example document. CSS requires the @import directive to come before any
other rules in a stylesheet. An @import that comes after other rules (e.g., body
{color: red;}) will be ignored by conforming user agents.
Older versions of Internet Explorer for Windows do not ignore any
@import directive, even those that come after other rules. Since
other browsers do ignore improperly placed @import directives, it
is easy to mistakenly place the @import directive incorrectly and
thus alter the display in other browsers.
HTTP Linking
There is another, far more obscure way to associate CSS with a document: you can
link the two via HTTP headers.
Under Apache, this can be accomplished by adding a reference to the CSS file in
a .htaccess file. For example:
Header add Link ";rel=stylesheet;type=text/css"
This will cause supporting browsers to associate the referenced stylesheet with any
documents served from under that .htaccess file. The browser will then treat it as if it
were a linked stylesheet. Alternatively, and probably more efficiently, you can add an
equivalent rule to the server’s httpd.conf file:
Header add Link ";rel=stylesheet;type=text/css"
14
|
Chapter 1: CSS and Documents
The effect is exactly the same in supporting browsers. The only difference is in where
you declare the linking.
You probably noticed the use of the term “supporting browsers.” As of late 2017, the
widely used browsers that support HTTP linking of stylesheets are the Firefox family
and Opera. That restricts this technique mostly to development environments based
on one of those browsers. In that situation, you can use HTTP linking on the test
server to mark when you’re on the development site as opposed to the public site. It’s
also an interesting way to hide styles from the WebKit and Internet Explorer families,
assuming you have a reason to do so.
There are equivalents to this technique in common scripting lan‐
guages such as PHP and IIS, both of which allow the author to emit
HTTP headers. It’s also possible to use such languages to explicitly
write a link element into the document based on the server offer‐
ing up the document. This is a more robust approach in terms of
browser support: every browser supports the link element.
Inline Styles
For cases where you want to just assign a few styles to one individual element,
without the need for embedded or external stylesheets, employ the HTML attribute
style to set an inline style:
The most wonderful of all breakfast foods is
the waffle—a ridged and cratered slab of home-cooked, fluffy goodness...
The style attribute can be associated with any HTML tag whatsoever, except for
those tags that are found outside of body (head or title, for instance).
The syntax of a style attribute is fairly ordinary. In fact, it looks very much like the
declarations found in the style container, except here the curly braces are replaced
by double quotation marks. So
will set the text color to be maroon and the background to be yellow for that para‐
graph only. No other part of the document will be affected by this declaration.
Note that you can only place a declaration block, not an entire stylesheet, inside an
inline style attribute. Therefore, you can’t put an @import into a style attribute, nor
can you include any complete rules. The only thing you can put into the value of a
style attribute is what might go between the curly braces of a rule.
Use of the style attribute is not generally recommended. Indeed, it is very unlikely to
appear in XML languages other than HTML. Many of the primary advantages of CSS
—the ability to organize centralized styles that control an entire document’s appear‐
ance or the appearance of all documents on a web server—are negated when you
Bringing CSS and HTML Together
|
15
place styles into a style attribute. In many ways, inline styles are not much better
than the font tag, although they do have a good deal more flexibility in terms of what
visual effects they can apply.
Stylesheet Contents
So after all of that, what about the actual contents of the stylesheets? You know, stuff
like this:
h1 {color: maroon;}
body {background: yellow;}
Styles such as these comprise the bulk of any embedded stylesheet—simple and com‐
plex, short and long. Rarely will you have a document where the style element does
not contain any rules, although it’s possible to have a simple list of @import declara‐
tions with no actual rules like those shown in the previous example.
Before we get going on the rest of the book, there are a few top-level things to cover
regarding what can or can’t go into a stylesheet.
Markup
There is no markup in stylesheets. This might seem obvious, but you’d be surprised.
The one exception is HTML comment markup, which is permitted inside style ele‐
ments for historical reasons:
That’s it.
Rule Structure
To illustrate the concept of rules in more detail, let’s break down the structure.
Each rule has two fundamental parts: the selector and the declaration block. The decla‐
ration block is composed of one or more declarations, and each declaration is a pair‐
ing of a property and a value. Every stylesheet is made up of a series of rules.
Figure 1-7 shows the parts of a rule.
16
|
Chapter 1: CSS and Documents
Figure 1-7. The structure of a rule
The selector, shown on the left side of the rule, defines which piece of the document
will be affected. In Figure 1-7, h1 elements are selected. If the selector were p, then all
p (paragraph) elements would be selected.
The right side of the rule contains the declaration block, which is made up of one or
more declarations. Each declaration is a combination of a CSS property and a value of
that property. In Figure 1-7, the declaration block contains two declarations. The first
states that this rule will cause parts of the document to have a color of red, and the
second states that part of the document will have a background of yellow. So, all of
the h1 elements in the document (defined by the selector) will be styled in red text
with a yellow background.
Vendor prefixing
Sometimes you’ll see pieces of CSS with dashes and labels in front of them, like this: o-border-image. These are called vendor prefixes, and are a way for browser vendors
to mark properties, values, or other bits of CSS as being experimental or proprietary
(or both). As of late 2016, there were a few vendor prefixes in the wild, with the most
common being shown in Table 1-1.
Table 1-1. Some common vendor prefixes
-epub-
Prefix
Vendor
International Digital Publishing Forum ePub format
-moz-
Mozilla-based browsers (e.g., Firefox)
-ms-
Microsoft Internet Explorer
-o-
Opera-based browsers
-webkit- WebKit-based browsers (e.g., Safari and Chrome)
As Table 1-1 implies, the generally accepted format of a vendor prefix is a dash, a
label, and a dash, although a few prefixes erroneously omit the first dash.
The uses and abuses of vendor prefixes are long, tortuous, and beyond the scope of
this book. Suffice to say that they started out as a way for vendors to test out new
Stylesheet Contents
|
17
features, thus helping speed interoperability without worrying about being locked
into legacy behaviors that were incompatible with other browsers. This avoided a
whole class of problems that nearly strangled CSS in its infancy. Unfortunately, pre‐
fixed properties were then publicly deployed by web authors and ended up causing a
whole new class of problems.
As of late 2016, vendor prefixes are a dwindling breed, with old prefixed properties
and values being slowly removed from browser implementations. It’s entirely possible
that you’ll never write prefixed CSS, but you may encounter it in the wild, or inherit it
in a legacy codebase.
Whitespace Handling
CSS is basically insensitive to whitespace between rules, and largely insensitive to
whitespace within rules, although there are a few exceptions.
In general, CSS treats whitespace just like HTML does: any sequence of whitespace
characters is collapsed to a single space for parsing purposes. Thus, you can format
the hypothetical rainbow rule in the following ways:
rainbow: infrared red orange yellow green blue indigo violet ultraviolet;
rainbow:
infrared red orange yellow green blue indigo violet ultraviolet;
rainbow:
infrared
red
orange
yellow
green
blue
indigo
violet
ultraviolet
;
…as well as any other separation patterns you can think up. The only restriction is
that the separating characters be whitespace: an empty space, a tab, or a newline,
alone or in combination, as many as you like.
Similarly, you can format series of rules with whitespace in any fashion you like.
These are just five of an effectively infinite number of possibilities:
html{color:black;}
body {background: white;}
p {
color: gray;}
h2 {
color : silver ;
}
ol
18
|
Chapter 1: CSS and Documents
{
color
:
silver
;
}
As you can see from the first rule, whitespace can be largely omitted. Indeed, this is
usually the case with minified CSS, which is CSS that’s had every last possible bit of
extraneous whitespace removed. The rules after the first two use progressively more
extravagant amounts of whitespace until, in the last rule, pretty much everything that
can be separated onto its own line has been.
All of these approaches are valid, so you should pick the formatting that makes the
most sense—that is, is easiest to read—in your eyes, and stick with it.
There are some places where the presence of whitespace is actually required. The
most common example is when separating a list of keywords in a value, as in the
hypothetical rainbow examples. Those must always be whitespace-separated.
CSS Comments
CSS does allow for comments. These are very similar to C/C++ comments in that
they are surrounded by /* and */:
/* This is a CSS1 comment */
Comments can span multiple lines, just as in C++:
/* This is a CSS1 comment, and it
can be several lines long without
any problem whatsoever. */
It’s important to remember that CSS comments cannot be nested. So, for example,
this would not be correct:
/* This is a comment, in which we find
another comment, which is WRONG
/* Another comment */
and back to the first comment */
One way to create “nested” comments accidentally is to temporarily
comment out a large block of a stylesheet that already contains a
comment. Since CSS doesn’t permit nested comments, the “outside”
comment will end where the “inside” comment ends.
Unfortunately, there is no “rest of the line” comment pattern such as // or # (the lat‐
ter of which is reserved for ID selectors anyway). The only comment pattern in CSS
is /* */. Therefore, if you wish to place comments on the same line as markup, then
Stylesheet Contents
|
19
you need to be careful about how you place them. For example, this is the correct way
to do it:
h1 {color: gray;}
h2 {color: silver;}
p {color: white;}
pre {color: gray;}
/*
/*
/*
/*
This CSS comment is several lines */
long, but since it is alongside */
actual styles, each line needs to */
be wrapped in comment markers. */
Given this example, if each line isn’t marked off, then most of the stylesheet will
become part of the comment and thus will not work:
h1 {color: gray;}
/* This CSS comment is several lines
h2 {color: silver;} long, but since it is not wrapped
p {color: white;}
in comment markers, the last three
pre {color: gray;}
styles are part of the comment. */
In this example, only the first rule (h1 {color: gray;}) will be applied to the docu‐
ment. The rest of the rules, as part of the comment, are ignored by the browser’s ren‐
dering engine.
CSS comments are treated by the CSS parser as if they do not exist
at all, and so do not count as whitespace for parsing purposes. This
means you can put them into the middle of rules—even right
inside declarations!
Media Queries
With media queries, an author can define the media environment in which a given
stylesheet is used by the browser. In the past, this was handled by setting media types
via the media attribute on the link element, on a style element, or in the media
descriptor of an @import or @media declaration. Media queries take this concept sev‐
eral steps further by allowing authors to choose stylesheets based on the features of a
given media type, using what are called media descriptors.
Usage
Media queries can be employed in the following places:
• The media attribute of a link element
• The media attribute of a style element
• The media descriptor portion of an @import declaration
• The media descriptor portion of an @media declaration
Queries can range from simple media types to complicated combinations of media
types and features.
20
|
Chapter 1: CSS and Documents
Simple Media Queries
Let’s look at some simple media blocks before covering all of the possibilities of media
queries. Suppose we want some differing styles for situations where the styles are
shown in a projection setting, such as a slide show. Here are two very simple bits of
CSS:
h1 {color: maroon;}
@media projection {
body {background: yellow;}
}
In this example, h1 elements will be colored maroon in all media, but the body ele‐
ment will get a yellow background only in a projection medium.
You can have as many @media blocks as you like in a given stylesheet, each with its
own set of media descriptors (see later in this chapter for details). You could even
encapsulate all of your rules in an @media block if you chose, like this:
@media all {
h1 {color: maroon;}
body {background: yellow;}
}
However, since this is exactly the same as if you stripped off the first and last line
shown, there isn’t a whole lot of point to doing so.
The indentation shown in this section was solely for purposes of
clarity. You do not have to indent the rules found inside an @media
block, but you’re welcome to do so if it makes your CSS easier for
you to read.
The place where we saw projection and all in those examples is where media quer‐
ies are set. These queries rely on a combination of terms that describe the type of
media to be considered, as well as descriptions of the media’s parameters (e.g., resolu‐
tion or display height), to determine when blocks of CSS should be applied.
Media Types
The most basic form of media queries are media types, which first appeared in CSS2.
These are simple labels for different kinds of media:
all
Use in all presentational media.
Media Queries
|
21
print
Use when printing the document for sighted users and also when displaying a
print preview of the document.
screen
Use when presenting the document in a screen medium like a desktop computer
monitor. All web browsers running on such systems are screen-medium user
agents.
As of this writing, a couple of browsers also support projection,
which allows a document to be presented as a slideshow. Several
mobile-device browsers support the handheld type, but not in con‐
sistent ways.
Multiple media types can be specified using a comma-separated list. The following
four examples are all equivalent ways of applying a stylesheet (or a block of rules) in
both screen and print media:
@import url(frobozz.css) screen, print;
@media screen, print {...}
Things get interesting when you add feature-specific descriptors, such as values that
describe the resolution or color depth of a given medium, to these media types.
Media Descriptors
The placement of media queries will be very familiar to any author who has ever set a
media type on a link element or an @import declaration. Here are two essentially
equivalent ways of applying an external stylesheet when rendering the document on a
color printer:
@import url(print-color.css) print and (color);
Anywhere a media type can be used, a media query can be used. This means that,
following on the examples of the previous section, it is possible to list more than one
query in a comma-separated list:
@import url(print-color.css) print and (color), screen and (color-depth: 8);
22
|
Chapter 1: CSS and Documents
In any situation where even one of the media queries evaluates to “true,” the associ‐
ated stylesheet is applied. Thus, given the previous @import, print-color.css will be
applied if rendering to a color printer or to a sufficiently colorful screen environment.
If printing on a black-and-white printer, both queries will evaluate to “false”, and
print-color.css will not be applied to the document. The same holds true in any
screen medium, and so on.
Each media descriptor is composed of a media type and one or more listed media fea‐
tures, with each media feature descriptor enclosed in parentheses. If no media type is
provided, then it is assumed to be all, which makes the following two examples
equivalent:
@media all and (min-resolution: 96dpi) {...}
@media (min-resolution: 96dpi) {...}
Generally speaking, a media feature descriptor is formatted like a property-value pair
in CSS. There are a few differences, most notably that some features can be specified
without an accompanying value. Thus, for example, any color-based medium will be
matched using (color), whereas any color medium using a 16-bit color depth is
matched using (color: 16). In effect, the use of a descriptor without a value is a
true/false test for that descriptor: (color) means “is this medium in color?”
Multiple feature descriptors can be linked with the and logical keyword. In fact, there
are two logical keywords in media queries:
and
Links together two or more media features in such a way that all of them must be
true for the query to be true. For example, (color) and (orientation: land
scape) and (min-device-width: 800px) means that all three conditions must
be satisfied: if the media environment has color, is in landscape orientation, and
the device’s display is at least 800 pixels wide, then the stylesheet is used.
not
Negates the entire query such that if all of the conditions are true, then the style‐
sheet is not applied. For example, not (color) and (orientation: landscape)
and (min-device-width: 800px) means that if the three conditions are satis‐
fied, the statement is negated. Thus, if the media environment has color, is in
landscape orientation, and the device’s display is at least 800 pixels wide, then the
stylesheet is not used. In all other cases, it will be used.
Note that the not keyword can only be used at the beginning of a media query. It is
not legal to write something like (color) and not (min-device-width: 800px). In
such cases, the query will be ignored. Note also that browsers too old to understand
media queries will always skip a stylesheet whose media descriptor starts with not.
Media Queries
|
23
There is no OR keyword for use in media queries. Instead, the commas that separate
a list of queries serve the function of an OR—screen, print means “apply if the
media is screen or print.” Instead of screen and (max-color: 2) or (monochrome),
which is invalid and thus ignored, you should write screen and (max-color: 2),
screen and (monochrome).
There is one more keyword, only, which is designed to create deliberate backward
incompatibility (yes, really):
only
Used to hide a stylesheet from browsers too old to understand media queries. For
example, to apply a stylesheet in all media, but only in those browsers that under‐
stand media queries, you write something like @import url(new.css) only
all. In browsers that do understand media queries, the only keyword is ignored
and the stylesheet is applied. In browsers that do not understand media queries,
the only keyword creates an apparent media type of only all, which is not valid.
Thus, the stylesheet is not applied in such browsers. Note that the only keyword
can only be used at the beginning of a media query.
Media Feature Descriptors and Value Types
So far we’ve seen a number of media feature descriptors in the examples, but not a
complete list. Following is a list of all possible descriptors (current as of late 2017):
• width
• aspect-ratio
• color-index
• min-width
• min-aspect-ratio
• min-color-index
• max-width
• max-aspect-ratio
• max-color-index
• device-width
• device-aspectratio
• monochrome
• min-device-aspectratio
• max-monochrome
• max-device-aspectratio
• min-resolution
• min-device-width
• max-device-width
• height
• min-height
• max-height
• device-height
• min-device-height
• max-device-height
24
|
Chapter 1: CSS and Documents
• color
• min-color
• max-color
• min-monochrome
• resolution
• max-resolution
• orientation
• scan
• grid
In addition, two new value types were added:
•
•
A complete description of these descriptors and values, and how to use them, can be
found in Chapter 20.
Feature Queries
Between 2015 and 2016, CSS gained the ability to apply blocks of CSS when certain
CSS property-value combinations were supported by the user agent. These are known
as feature queries.
They’re very similar to media queries in structure. Consider a situation where you
want to only apply color to element if color is a supported property. (Which it cer‐
tainly should be!) That would look like this:
@supports (color: black) {
body {color: black;}
h1 {color: purple;}
h2 {color: navy;}
}
That says, in effect, “If you recognize and can do something with the property-value
combination color: black, apply these styles. Otherwise, skip these styles.” In user
agents that don’t understand @supports, the entire block is skipped over.
Feature queries are a perfect way to progressively enhance your styles. For example,
suppose you want to add some grid layout to your existing float-and-inline-block lay‐
out. You can keep the old layout scheme, and then later in the stylesheet include a
block like this:
@supports (display: grid ) {
section#main {display: grid;}
/* styles to switch off old layout positioning */
/* grid layout styles */
}
This block of styles will be applied in browsers that understand grid display, overrid‐
ing the old styles that governed page layout, and then applying the styles needed to
make things work in a grid-based future. Browsers too old to understand grid layout
will most likely also be too old to understand @supports, so they’ll skip the whole
block entirely, as if it had never been there.
Feature queries can be embedded inside each other, and indeed can be embedded
inside media queries, as well as vice versa. You could write screen and print styles
Feature Queries
|
25
based on flexible-box layout, and wrap those media blocks in an @supports (dis
play: flex) block:
@supports (display: flex) {
@media screen {
/* screen flexbox styles go here */
}
@media print {
/* print flexbox styles go here */
}
}
Conversely, you could add @supports() blocks inside various responsive-design
media query blocks:
@media screen and (max-width: 30em){
@supports (display: flex) {
/* small-screen flexbox styles go here */
}
}
@media screen and (min-width: 30em) {
@supports (display: flex) {
/* large-screen flexbox styles go here */
}
}
How you organize these blocks is really up to you.
As with media queries, feature queries also permit logical operators. Suppose we want
to apply styles only if a user agent supports both grid layout and CSS shapes. Here’s
how that might go:
@supports (display: grid) and (shape-outside: circle()) {
/* grid-and-shape styles go here */
}
This is essentially equivalent to writing the following:
@supports (display: grid) {
@supports (shape-outside: circle()) {
/* grid-and-shape styles go here */
}
}
However, there’s more than “and” operations available. CSS Shapes (covered in detail
in Chapter 10) are a good example of why “or” is useful, because for a long time Web‐
Kit only supported CSS shapes via vendor-prefixed properties. So if you want to use
shapes, you can use a feature query like this:
@supports (shape-outside: circle()) or
(-webkit-shape-outside: circle()) {
/* shape styles go here */
}
26
|
Chapter 1: CSS and Documents
You’d still have to make sure to use both prefixed and unprefixed versions of the
shape properties, but this would let you add support for those properties backward in
the WebKit release line while supporting other browsers that also support shapes, but
not via prefixed properties.
All this is incredibly handy because there are situations where you might want to
apply different properties than those you’re testing. So, to go back to grid layout for a
second, you might want to change the margins and so forth on your layout elements
when grid is in use. Here’s a simplified version of that approach:
div#main {overflow: hidden;}
div.column {float: left; margin-right: 1em;}
div.column:last-child {margin-right: 0;}
@supports (display: grid) {
div#main {display: grid; grid-gap: 1em 0;
overflow: visible;}
div#main div.column {margin: 0;}
}
It’s possible to use negation as well. For example, you could apply the following styles
in situations where grid layout is not supported:
@supports not (display: grid) {
/* grid-not-supported styles go here */
}
You can combine your logical operators into a single query, but parentheses are
required to keep the logic straight. Suppose we want a set of styles to be applied when
color is supported, and when one of either grid or flexible box layout is supported.
That’s written like this:
@supports (color: black) and ((display: flex) or (display: grid)) {
/* styles go here */
}
Notice how there’s another set of parentheses around the “or” part of the logic,
enclosing the grid and flex tests. Those extra parentheses are required. Without them,
the entire expression will fail, and the styles inside the block will be skipped. In other
words, don’t do this:
@supports (color: black) and (display: flex) or (display: grid) {
Finally, you might wonder why both a property and value are required in feature
query tests. After all, if you’re using shapes, all you need to test for is shape-outside,
right? It’s because a browser can easily support a property without supporting all its
values. Grid layout is a perfect example for this. Suppose you could test for grid sup‐
port like this:
Feature Queries
|
27
@supports (display) {
/* grid styles go here */
}
Well, even Internet Explorer 4 supported display. Any browser that understands
@supports will certainly understand display and many of its values—but maybe not
grid. That’s why property and value are always tested in feature queries.
Remember that these are feature queries, not correctness queries. A
browser can understand the feature you’re testing for, but imple‐
ment it with bugs. So you’re not getting an assurance from the
browser that it supports something correctly. All a positive featurequery result means is that the browser understands what you’ve
said and makes some sort of attempt to support it.
Summary
With CSS, it is possible to completely change the way elements are presented by a
user agent. This can be executed at a basic level with the display property, and in a
different way by associating stylesheets with a document. The user will never know
whether this is done via an external or embedded stylesheet, or even with an inline
style. The real importance of external stylesheets is the way in which they allow
authors to put all of a site’s presentation information in one place, and point all of the
documents to that place. This not only makes site updates and maintenance a breeze,
but it helps to save bandwidth, since all of the presentation is removed from docu‐
ments. With @supports(), it’s even possible to do some basic progressive enhance‐
ment in native CSS.
To make the most of the power of CSS, authors need to know how to associate a set of
styles with the elements in a document. To fully understand how CSS can do all of
this, authors need a firm grasp of the way CSS selects pieces of a document for styl‐
ing, which is the subject of the next chapter.
28
|
Chapter 1: CSS and Documents
CHAPTER 2
Selectors
One of the primary advantages of CSS is its ability to easily apply a set of styles to all
elements of the same type. Unimpressed? Consider this: by editing a single line of
CSS, you can change the colors of all your headings. Don’t like the blue you’re using?
Change that one line of code, and they can all be purple, yellow, maroon, or any other
color you desire. That lets you, the designer, focus on design, rather than grunt work.
The next time you’re in a meeting and someone wants to see headings with a different
shade of green, just edit your style and hit Reload. Voilà! The results are accomplished
in seconds and there for everyone to see.
CSS can’t solve all your problems—you can’t use it to change the colorspace of your
PNGs, for example, at least not yet—but it can make some global changes much eas‐
ier. So let’s begin with selectors and structure.
Basic Style Rules
As stated, a central feature of CSS is its ability to apply certain rules to an entire set of
element types in a document. For example, let’s say that you want to make the text of
all h2 elements appear gray. Using old-school HTML, you’d have to do this by insert‐
ing … tags inside all your h2 elements. Using the style
attribute, which is also bad practice, would require you to include style="color:
gray;" in all your h2 elements, like this:
This is h2 text
This is h2 text
This will be a tedious process if your document contains a lot of h2 elements. Worse,
if you later decide that you want all those h2s to be green instead of gray, you’d have to
start the manual tagging all over again. (Yes, this is really how it used to be done!)
29
CSS allows you to create rules that are simple to change, edit, and apply to all the text
elements you define (the next section will explain how these rules work). For exam‐
ple, you can write this rule once to make all your h2 elements gray:
h2 {color: gray;}
If you want to change all h2 text to another color—say, silver—just alter the value:
h2 {color: silver;}
Element Selectors
An element selector is most often an HTML element, but not always. For example, if a
CSS file contains styles for an XML document, the element selectors might look
something like this:
quote {color: gray;}
bib {color: red;}
booktitle {color: purple;}
myElement {color: red;}
In other words, the elements of the document serve as the most basic selectors. In
XML, a selector could be anything, since XML allows for the creation of new markup
languages that can have just about anything as an element name. If you’re styling an
HTML document, on the other hand, the selector will generally be one of the many
HTML elements such as p, h3, em, a, or even html itself. For example:
html {color: black;}
h1 {color: gray;}
h2 {color: silver;}
The results of this stylesheet are shown in Figure 2-1.
Figure 2-1. Simple styling of a simple document
30
|
Chapter 2: Selectors
Once you’ve globally applied styles directly to elements, you can shift those styles
from one element to another. Let’s say you decide that the paragraph text, not the h1
elements, in Figure 2-1 should be gray. No problem. Just change the h1 selector to p:
html {color: black;}
p {color: gray;}
h2 {color: silver;}
The results are shown in Figure 2-2.
Figure 2-2. Moving a style from one element to another
Declarations and Keywords
The declaration block contains one or more declarations. A declaration is always for‐
matted as a property followed by a colon and then a value followed by a semicolon.
The colon and semicolon can be followed by zero or more spaces. In nearly all cases,
a value is either a single keyword or a space-separated list of one or more keywords
that are permitted for that property. If you use an incorrect property or value in a
declaration, the whole rule will be ignored. Thus, the following two declarations
would fail:
brain-size: 2cm;
color: ultraviolet;
/* unknown property 'brain-size' */
/* unknown value 'ultraviolet' */
In an instance where you can use more than one keyword for a property’s value, the
keywords are usually separated by spaces, with some cases requiring slashes (/) or
commas. Not every property can accept multiple keywords, but many, such as the
font property, can. Let’s say you want to define medium-sized Helvetica for para‐
graph text, as illustrated in Figure 2-3.
Basic Style Rules
|
31
Figure 2-3. The results of a property value with multiple keywords
The rule would read as follows:
p {font: medium Helvetica;}
Note the space between medium and Helvetica, each of which is a keyword (the first
is the font’s size and the second is the actual font name). The space allows the user
agent to distinguish between the two keywords and apply them correctly. The semi‐
colon indicates that the declaration has been concluded.
These space-separated words are referred to as keywords because, taken together, they
form the value of the property in question. For instance, consider the following fic‐
tional rule:
rainbow: red orange yellow green blue indigo violet;
There is no such property as rainbow, but the example is useful for illustrative pur‐
poses. The value of rainbow is red orange yellow green blue indigo violet, and
the seven keywords add up to a single, unique value. We can redefine the value for
rainbow as follows:
rainbow: infrared red orange yellow green blue indigo violet ultraviolet;
Now we have a new value for rainbow composed of nine keywords instead of seven.
Although the two values look mostly the same, they are as unique and different as
zero and one. This may seem an abstract point, but it’s critical to understanding some
of the subtler effects of specificity and the cascade (covered in later in this book).
As we’ve seen, CSS keywords are usually separated by spaces. In CSS2.1 there was one
exception: in the CSS property font, there is exactly one place where a forward slash
(/) could be used to separate two specific keywords. Here’s an example:
h2 {font: large/150% sans-serif;}
32
|
Chapter 2: Selectors
The slash separates the keywords that set the element’s font size and line height. This
is the only place the slash is allowed to appear in the font declaration. All of the other
keywords allowed for font are separated by spaces.
The slash has since worked its way into a number of other properties’ values. These
include, but may not always be limited to the following:
• background
• border-image
• border-radius
• grid
• grid-area
• grid-column
• grid-row
• grid-template
• mask-border
There are also some keywords that are separated by commas. When declaring multi‐
ple values, such as multiple background images, transition properties, and shadows,
the declarations are separated with commas. Additionally, parameters in functions,
such as linear gradients and transforms, are comma separated, as the following exam‐
ple shows:
.box {box-shadow: inset -1px -1px white,
3px 3px 3px rgba(0,0,0,0.2);
background-image: url(myimage.png),
linear-gradient(180deg, #FFF 0%, #000 100%);
transform: translate(100px, 200px);
}
a:hover {transition: color, background-color 200ms ease-in 50ms;}
Those are the basics of simple declarations, but they can get much more complex.
The next section begins to show you just how powerful CSS can be.
Grouping
So far, we’ve seen fairly simple techniques for applying a single style to a single selec‐
tor. But what if you want the same style to apply to multiple elements? If that’s the
case, you’ll want to use more than one selector or apply more than one style to an
element or group of elements.
Grouping
|
33
Grouping Selectors
Let’s say you want both h2 elements and paragraphs to have gray text. The easiest way
to accomplish this is to use the following declaration:
h2, p {color: gray;}
By placing the h2 and p selectors on the left side of the rule and separating them with
a comma, you’ve defined a rule where the style on the right (color: gray;) applies to
the elements referenced by both selectors. The comma tells the browser that there are
two different selectors involved in the rule. Leaving out the comma would give the
rule a completely different meaning, which we’ll explore in “Descendant Selectors” on
page 56.
There are really no limits to how many selectors you can group together. For exam‐
ple, if you want to display a large number of elements in gray, you might use some‐
thing like the following rule:
body, table, th, td, h1, h2, h3, h4, p, pre, strong, em, b, i {color: gray;}
Grouping allows an author to drastically compact certain types of style assignments,
which makes for a shorter stylesheet. The following alternatives produce exactly the
same result, but it’s pretty obvious which one is easier to type:
h1
h2
h3
h4
h5
h6
{color:
{color:
{color:
{color:
{color:
{color:
purple;}
purple;}
purple;}
purple;}
purple;}
purple;}
h1, h2, h3, h4, h5, h6 {color: purple;}
Grouping allows for some interesting choices. For example, all of the groups of rules
in the following example are equivalent—each merely shows a different way of group‐
ing both selectors and declarations:
/* group 1 */
h1 {color: silver; background: white;}
h2 {color: silver; background: gray;}
h3 {color: white; background: gray;}
h4 {color: silver; background: white;}
b {color: gray; background: white;}
/* group 2 */
h1, h2, h4 {color: silver;}
h2, h3 {background: gray;}
h1, h4, b {background: white;}
h3 {color: white;}
b {color: gray;}
34
|
Chapter 2: Selectors
/* group 3 */
h1, h4 {color: silver; background: white;}
h2 {color: silver;}
h3 {color: white;}
h2, h3 {background: gray;}
b {color: gray; background: white;}
Any of these will yield the result shown in Figure 2-4. (These styles use grouped dec‐
larations, which are explained in “Grouping Declarations” on page 35.)
Figure 2-4. The result of equivalent stylesheets
The universal selector
CSS2 introduced a new simple selector called the universal selector, displayed as an
asterisk (*). This selector matches any element at all, much like a wildcard. For exam‐
ple, to make every single element in a document red, you would write:
* {color: red;}
This declaration is equivalent to a grouped selector that lists every single element
contained within the document. The universal selector lets you assign the color value
red to every element in the document in one efficient stroke. Beware, however:
although the universal selector is convenient, with a specificity on 0-0-0; and because
it targets everything within its declaration scope, it can have unintended conse‐
quences, which are discussed later in this book.
Grouping Declarations
Since you can group selectors together into a single rule, it follows that you can also
group declarations. Assuming that you want all h1 elements to appear in purple, 18pixel-high Helvetica text on an aqua background (and you don’t mind blinding your
readers), you could write your styles like this:
Grouping
|
35
h1 {font: 18px Helvetica;}
h1 {color: purple;}
h1 {background: aqua;}
But this method is inefficient—imagine creating such a list for an element that will
carry 10 or 15 styles! Instead, you can group your declarations together:
h1 {font: 18px Helvetica; color: purple; background: aqua;}
This will have exactly the same effect as the three-line stylesheet just shown.
Note that using semicolons at the end of each declaration is crucial when you’re
grouping them. Browsers ignore whitespace in stylesheets, so the user agent must rely
on correct syntax to parse the stylesheet. You can fearlessly format styles like the
following:
h1 {
font: 18px Helvetica;
color: purple;
background: aqua;
}
You can also minimize your CSS, removing all non-required spaces.
h1{font:18px Helvetica;color:purple;background:aqua;}
Ignoring whitespace, the last three are treated equally by the server, but the second
one is most human readable, and the recommended method of marking up your CSS
during development. (You might choose to minimize your CSS for networkperformance reasons, but this is usually handled by a server-side script, caching net‐
work, or other service.)
If the semicolon is omitted on the second statement, the user agent will interpret the
stylesheet as follows:
h1 {
font: 18px Helvetica;
color: purple background: aqua;
}
Because background: is not a valid value for color, and because color can be given
only one keyword, a user agent will ignore the color declaration (including the back
ground: aqua part) entirely. You might think the browser would at least render h1s as
purple text without an aqua background, but not so. Instead, they will be the default
color (which is usually black) with a transparent background (which is also a default).
The declaration font: 18px Helvetica will still take effect since it was correctly ter‐
minated with a semicolon.
36
|
Chapter 2: Selectors
Although it is not technically necessary to follow the last declara‐
tion of a rule with a semicolon in CSS, it is generally good practice
to do so. First, it will keep you in the habit of terminating your dec‐
larations with semicolons, the lack of which is one of the most
common causes of rendering errors. Second, if you decide to add
another declaration to a rule, you won’t have to worry about forget‐
ting to insert an extra semicolon. Third, if you ever use a prepro‐
cessor like Sass, trailing semicolons are often required for all
declarations. Avoid all these problems—always follow a declaration
with a semicolon, wherever the rule appears.
As with selector grouping, declaration grouping is a convenient way to keep your
stylesheets short, expressive, and easy to maintain.
Grouping Everything
You now know that you can group selectors and you can group declarations. By com‐
bining both kinds of grouping in single rules, you can define very complex styles
using only a few statements. Now, what if you want to assign some complex styles to
all the headings in a document, and you want the same styles to be applied to all of
them? Here’s how to do it:
h1, h2, h3, h4, h5, h6 {color: gray; background: white; padding: 0.5em;
border: 1px solid black; font-family: Charcoal, sans-serif;}
Here we’ve grouped the selectors, so the styles on the right side of the rule will be
applied to all the headings listed; grouping the declarations means that all of the listed
styles will be applied to the selectors on the left side of the rule. The result of this rule
is shown in Figure 2-5.
Figure 2-5. Grouping both selectors and rules
Grouping
|
37
This approach is preferable to the drawn-out alternative, which would begin with
something like this:
h1
h2
h3
h4
h5
h6
h1
h2
h3
{color: gray;}
{color: gray;}
{color: gray;}
{color: gray;}
{color: gray;}
{color: gray;}
{background: white;}
{background: white;}
{background: white;}
and continue for many lines. You can write out your styles the long way, but I
wouldn’t recommend it—editing them would be as tedious as using style attributes
everywhere!
It’s possible to add even more expression to selectors and to apply styles in a way that
cuts across elements in favor of types of information. To get something so powerful,
you’ll have to do a little work in return, but it’s well worth it.
New Elements in Old Browsers
With updates to HTML, such as the HTML5 specification, new elements have come
into being. Some browsers predate these newer elements, and so don’t recognize
them. Versions of Internet Explorer prior to IE9, for example, did not support select‐
ing elements they did not understand. The solution was to create the element in the
DOM, thereby informing the browser that said element exists.
For example, IE8 does not recognize the element. The following JavaScript
line informs IE8 of main’s existence:
document.createElement('main');
By running that line of code, older versions of Internet Explorer will recognize the
existence of the element, allowing it to be selected and styled.
Class and ID Selectors
So far, we’ve been grouping selectors and declarations together in a variety of ways,
but the selectors we’ve been using are very simple ones that refer only to document
elements. Element selectors are fine up to a point, but there are times when you need
something a little more specialized.
In addition to raw document elements, there are class selectors and ID selectors, which
let you assign styles in a way that is independent of document elements. These selec‐
tors can be used on their own or in conjunction with element selectors. However,
38
|
Chapter 2: Selectors
they work only if you’ve marked up your document appropriately, so using them gen‐
erally involves a little forethought and planning.
For example, say you’re drafting a document that discusses ways of handling pluto‐
nium. The document contains various warnings about safely dealing with such a dan‐
gerous substance. You want each warning to appear in boldface text so that it will
stand out. However, you don’t know which elements these warnings will be. Some
warnings could be entire paragraphs, while others could be a single item within a
lengthy list or a small section of text. So, you can’t define a rule using element selec‐
tors of any kind. Suppose you tried this route:
p {
font-weight: bold;
color: red;
}
All paragraphs would be red and bold, not just those that contain warnings. You need
a way to select only the text that contains warnings, or more precisely, a way to select
only those elements that are warnings. How do you do it? You apply styles to parts of
the document that have been marked in a certain way, independent of the elements
involved, by using class selectors.
Class Selectors
The most common way to apply styles without worrying about the elements involved
is to use class selectors. Before you can use them, however, you need to modify your
actual document markup so that the class selectors will work. Enter the class
attribute:
When handling plutonium, care must be taken to avoid
the formation of a critical mass.
With plutonium, the possibility of implosion is
very real, and must be avoided at all costs. This can be accomplished
by keeping the various masses separate.
To associate the styles of a class selector with an element, you must assign a class
attribute the appropriate value. In the previous code block, a class value of warning
was assigned to two elements: the first paragraph and the span element in the second
paragraph.
All you need now is a way to apply styles to these classed elements. In HTML docu‐
ments, you can use a compact notation where the name of a class is preceded by a
period (.) and can be joined with an element selector:
.warning {font-weight: bold;}
When combined with the example markup shown earlier, this simple rule has the
effect shown in Figure 2-6. That is, the declaration font-weight: bold will be
Class and ID Selectors
|
39
applied to every element (thanks to the presence of the implicit universal selector)
that carries a class attribute with a value of warning.
The universal selector, represented by *, is implied when an ID,
class, attribute selector, pseudo-class or pseudo-element selector is
written without being attached to an element selector.
Figure 2-6. Using a class selector
As you can see, the class selector works by directly referencing a value that will be
found in the class attribute of an element. This reference is always preceded by a
period (.), which marks it as a class selector. The period helps keep the class selector
separate from anything with which it might be combined—such as an element selec‐
tor. For example, you may want boldface text only when an entire paragraph is a
warning:
p.warning {font-weight: bold;}
The selector now matches any p elements that have a class attribute containing the
word warning, but no other elements of any kind, classed or otherwise. Since the
span element is not a paragraph, the rule’s selector doesn’t match it, and it won’t be
displayed using boldfaced text.
If you did want to assign different styles to the span element, you could use the selec‐
tor span.warning:
p.warning {font-weight: bold;}
span.warning {font-style: italic;}
40
|
Chapter 2: Selectors
In this case, the warning paragraph is boldfaced, while the warning span is italicized.
Each rule applies only to a specific type of element/class combination, so it does not
leak over to other elements.
Another option is to use a combination of a general class selector and an elementspecific class selector to make the styles even more useful, as in the following markup:
.warning {font-style: italic;}
span.warning {font-weight: bold;}
The results are shown in Figure 2-7.
Figure 2-7. Using generic and specific selectors to combine styles
In this situation, any warning text will be italicized, but only the text within a span
element with a class of warning will be both boldfaced and italicized.
Notice the format of the general class selector in the previous example: it’s a class
name preceded by a period without any element name, and no universal selector. In
cases where you only want to select all elements that share a class name, you can omit
the universal selector from a class selector without any ill effects.
Multiple Classes
In the previous section, we dealt with class values that contained a single word. In
HTML, it’s possible to have a space-separated list of words in a single class value.
For example, if you want to mark a particular element as being both urgent and a
warning, you could write:
When handling plutonium, care must be taken to
avoid the formation of a critical mass.
With plutonium, the possibility of implosion is
very real, and must be avoided at all costs. This can be accomplished
by keeping the various masses separate.
Class and ID Selectors
|
41
The order of the words doesn’t matter; warning urgent would also suffice and would
yield precisely the same results no matter how the CSS class attribute is written.
Now let’s say you want all elements with a class of warning to be boldfaced, those
with a class of urgent to be italic, and those elements with both values to have a sil‐
ver background. This would be written as follows:
.warning {font-weight: bold;}
.urgent {font-style: italic;}
.warning.urgent {background: silver;}
By chaining two class selectors together, you can select only those elements that have
both class names, in any order. As you can see, the HTML source contains
class="urgent warning" but the CSS selector is written .warning.urgent. Regard‐
less, the rule will still cause the “When handling plutonium . . . ” paragraph to have a
silver background, as illustrated in Figure 2-8. This happens because the order the
words are written in doesn’t matter. (This is not to say the order of classes is always
irrelevant, but we’ll get to that later in the book.)
Figure 2-8. Selecting elements with multiple class names
If a multiple class selector contains a name that is not in the space-separated list, then
the match will fail. Consider the following rule:
p.warning.help {background: red;}
As you would expect, the selector will match only those p elements with a class con‐
taining the words warning and help. Therefore, it will not match a p element with
just the words warning and urgent in its class attribute. It would, however, match
the following:
Help me!
42
| Chapter 2: Selectors
ID Selectors
In some ways, ID selectors are similar to class selectors, but there are a few crucial dif‐
ferences. First, ID selectors are preceded by an octothorpe (#)—also known as a
pound sign (in the US), hash sign, hash mark, or tic-tac-toe board—instead of a
period. Thus, you might see a rule like this one:
*#first-para {font-weight: bold;}
This rule produces boldfaced text in any element whose id attribute has a value of
first-para.
The second difference is that instead of referencing values of the class attribute, ID
selectors refer, unsurprisingly, to values found in id attributes. Here’s an example of
an ID selector in action:
*#lead-para {font-weight: bold;}
This paragraph will be boldfaced.
This paragraph will NOT be bold.
Note that the value lead-para could have been assigned to any element within the
document. In this particular case, it is applied to the first paragraph, but we could
have applied it just as easily to the second or third paragraph. Or an unordered list.
Or anything.
As with class selectors, it is possible to omit the universal selector from an ID selector.
In the previous example, we could also have written:
#lead-para {font-weight: bold;}
The effect of this selector would be the same.
Another similarity between classes and IDs is that IDs can be selected independently
of an element. There may be circumstances in which you know that a certain ID value
will appear in a document, but you don’t know the element on which it will appear (as
in the plutonium-handling warnings), so you’ll want to declare standalone ID selec‐
tors. For example, you may know that in any given document, there will be an ele‐
ment with an ID value of mostImportant. You don’t know whether that most
important thing will be a paragraph, a short phrase, a list item, or a section heading.
You know only that it will exist in each document, occur in an arbitrary element, and
appear no more than once. In that case, you would write a rule like this:
#mostImportant {color: red; background: yellow;}
This rule would match any of the following elements (which, as noted before, should
not appear together in the same document because they all have the same ID value):
This is important!
This is important!
This is important!
Class and ID Selectors
|
43
Deciding Between Class and ID
You may assign classes to any number of elements, as demonstrated earlier; the class
name warning was applied to both a p and a span element, and it could have been
applied to many more elements. IDs, on the other hand, should be used once, and
only once, within an HTML document. Therefore, if you have an element with an id
value of lead-para, no other element in that document should have an id value of
lead-para.
In the real world, browsers don’t always check for the uniqueness
of IDs in HTML. That means that if you sprinkle an HTML docu‐
ment with several elements, all of which have the same value for
their ID attributes, you’ll probably get the same styles applied to
each. This is incorrect behavior, but it happens anyway. Having
more than one of the same ID value in a document also makes
DOM scripting more difficult, since functions like getElement
ById() depend on there being one, and only one, element with a
given ID value.
Unlike class selectors, ID selectors can’t be combined with other IDs, since ID
attributes do not permit a space-separated list of words.
Another difference between class and id names is that IDs carry more weight when
you’re trying to determine which styles should be applied to a given element. This
will be explained in greater detail in the next chapter.
Also note that class and ID selectors may be case-sensitive, depending on the docu‐
ment language. HTML defines class and ID values to be case-sensitive, so the capitali‐
zation of your class and ID values must match that found in your documents. Thus,
in the following pairing of CSS and HTML, the element’s text will not be boldfaced:
p.criticalInfo {font-weight: bold;}
Don't look down.
Because of the change in case for the letter i, the selector will not match the element
shown.
On a purely syntactical level, the dot-class notation (e.g., .warning) is not guaranteed
to work for XML documents. As of this writing, the dot-class notation works in
HTML, SVG, and MathML, and it may well be permitted in future languages, but it’s
up to each language’s specification to decide that. The hash-ID notation (e.g., #lead)
will work in any document language that has an attribute that enforces uniqueness
within a document. Uniqueness can be enforced with an attribute called id, or indeed
anything else, as long as the attribute’s contents are defined to be unique within the
document.
44
|
Chapter 2: Selectors
Attribute Selectors
When it comes to both class and ID selectors, what you’re really doing is selecting
values of attributes. The syntax used in the previous two sections is particular to
HTML, XHTML, SVG, and MathML documents (as of this writing). In other markup
languages, these class and ID selectors may not be available (as, indeed, those
attributes may not be present). To address this situation, CSS2 introduced attribute
selectors, which can be used to select elements based on their attributes and the values
of those attributes. There are four general types of attribute selectors: simple attribute
selectors, exact attribute value selectors, partial-match attribute value selectors, and
leading-value attribute selectors.
Simple Attribute Selectors
If you want to select elements that have a certain attribute, regardless of that attrib‐
ute’s value, you can use a simple attribute selector. For example, to select all h1 ele‐
ments that have a class attribute with any value and make their text silver, write:
h1[class] {color: silver;}
So, given the following markup:
Hello
Serenity
Fooling
you get the result shown in Figure 2-9.
Figure 2-9. Selecting elements based on their attributes
This strategy is very useful in XML documents, as XML languages tend to have ele‐
ment and attribute names that are specific to their purpose. Consider an XML lan‐
guage that is used to describe planets of the solar system (we’ll call it PlanetML). If
you want to select all pml-planet elements with a moons attribute and make them
boldface, thus calling attention to any planet that has moons, you would write:
pml-planet[moons] {font-weight: bold;}
Attribute Selectors
|
45
This would cause the text of the second and third elements in the following markup
fragment to be boldfaced, but not the first:
VenusEarthMars
In HTML documents, you can use this feature in a number of creative ways. For
example, you could style all images that have an alt attribute, thus highlighting those
images that are correctly formed:
img[alt] {border: 3px solid red;}
(This particular example is generally useful more for diagnostic purposes—that is,
determining whether images are indeed correctly marked up—than for design
purposes.)
If you wanted to boldface any element that includes title information, which most
browsers display as a “tool tip” when a cursor hovers over the element, you could
write:
*[title] {font-weight: bold;}
Similarly, you could style only those anchors (a elements) that have an href attribute,
thus applying the styles to any hyperlink but not to any placeholder anchors.
It is also possible to select based on the presence of more than one attribute. You do
this by chaining the attribute selectors together. For example, to boldface the text of
any HTML hyperlink that has both an href and a title attribute, you would write:
a[href][title] {font-weight: bold;}
This would boldface the first link in the following markup, but not the second or
third:
W3C Standards Info dead.letter
Selection Based on Exact Attribute Value
You can further narrow the selection process to encompass only those elements
whose attributes are a certain value. For example, let’s say you want to boldface any
hyperlink that points to a certain document on the web server. This would look
something like:
a[href="http://www.css-discuss.org/about.html"] {font-weight: bold;}
This will boldface the text of any a element that has an href attribute with exactly the
value http://www.css-discuss.org/about.html. Any change at all, even dropping the
www. part or changing to a secure protocol with https, will prevent a match.
46
| Chapter 2: Selectors
Any attribute and value combination can be specified for any element. However, if
that exact combination does not appear in the document, then the selector won’t
match anything. Again, XML languages can benefit from this approach to styling.
Let’s return to our PlanetML example. Suppose you want to select only those planet
elements that have a value of 1 for the attribute moons:
planet[moons="1"] {font-weight: bold;}
This would boldface the text of the second element in the following markup frag‐
ment, but not the first or third:
VenusEarthMars
As with attribute selection, you can chain together multiple attribute-value selectors
to select a single document. For example, to double the size of the text of any HTML
hyperlink that has both an href with a value of http://www.w3.org/ and a title
attribute with a value of W3C Home, you would write:
a[href="http://www.w3.org/"][title="W3C Home"] {font-size: 200%;}
This would double the text size of the first link in the following markup, but not the
second or third:
W3C Standards Info dead.link
The results are shown in Figure 2-10.
Figure 2-10. Selecting elements based on attributes and their values
Again, this format requires an exact match for the attribute’s value. Matching
becomes an issue when the selector form encounters values that can in turn contain a
space-separated list of values (e.g., the HTML attribute class). For example, consider
the following markup fragment:
Mercury
The only way to match this element based on its exact attribute value is to write:
planet[type="barren rocky"] {font-weight: bold;}
Attribute Selectors
|
47
If you were to write planet[type="barren"], the rule would not match the example
markup and thus would fail. This is true even for the class attribute in HTML. Con‐
sider the following:
When handling plutonium, care must be taken to
avoid the formation of a critical mass.
To select this element based on its exact attribute value, you would have to write:
p[class="urgent warning"] {font-weight: bold;}
This is not equivalent to the dot-class notation covered earlier, as we will see in the
next section. Instead, it selects any p element whose class attribute has exactly the
value "urgent warning", with the words in that order and a single space between
them. It’s effectively an exact string match.
Also, be aware that ID selectors and attribute selectors that target the id attribute are
not precisely the same. In other words, there is a subtle but crucial difference between
h1#page-title and h1[id="page-title"]. This difference is explained in “Specific‐
ity” on page 97.
Selection Based on Partial Attribute Values
Odds are that you’ll want to select elements based on portions of their attribute val‐
ues, rather than the full value. For such situations, CSS actually offers a variety of
options for matching substrings in an attribute’s value. These are summarized in
Table 2-1.
Table 2-1. Substring matching with attribute selectors
Type
Description
[foo~="bar"] Selects any element with an attribute foo whose value contains the word bar in a space-separated list
of words
[foo*="bar"] Selects any element with an attribute foo whose value contains the substring bar
[foo^="bar"] Selects any element with an attribute foo whose value begins with bar
[foo$="bar"] Selects any element with an attribute foo whose value ends with bar
[foo|="bar"] Selects any element with an attribute foo whose value starts with bar followed by a dash (U+002D) or
whose value is exactly equal to bar
A Particular Attribute Selection Type
The first of these attribute selectors that match on a partial subset of an element’s
attribute value is actually easier to show than it is to describe. Consider the following
rule:
*[lang|="en"] {color: white;}
48
|
Chapter 2: Selectors
This rule will select any element whose lang attribute is equal to en or begins with
en-. Therefore, the first three elements in the following example markup would be
selected, but the last two would not:
Hello!
Greetings!
G'day!
Bonjour!
Jrooana!
In general, the form [att|="val"] can be used for any attribute and its values. Let’s
say you have a series of figures in an HTML document, each of which has a filename
like figure-1.gif and figure-3.jpg. You can match all of these images using the following
selector:
img[src|="figure"] {border: 1px solid gray;}
Or, if you’re creating a CSS framework or pattern library, instead of creating redun‐
dant classes like "btn btn-small btn-arrow btn-active", you can declare "btnsmall-arrow-active", and target the class of elements with:
*[class|="btn"] { border-radius: 5px;}
The most common use for this type of attribute selector is to match language values,
as demonstrated in an upcoming section, “The :lang Pseudo-Class” on page 88.
Matching one word in a space-separated list
For any attribute that accepts a space-separated list of words, it is possible to select
elements based on the presence of any one of those words. The classic example in
HTML is the class attribute, which can accept one or more words as its value. Con‐
sider our usual example text:
When handling plutonium, care must be taken to
avoid the formation of a critical mass.
Let’s say you want to select elements whose class attribute contains the word warn
ing. You can do this with an attribute selector:
p[class~="warning"] {font-weight: bold;}
Note the presence of the tilde (~) in the selector. It is the key to selection based on the
presence of a space-separated word within the attribute’s value. If you omit the tilde,
you would have an exact value-matching attribute selector, as discussed in the previ‐
ous section.
This selector construct is equivalent to the dot-class notation discussed in “Deciding
Between Class and ID” on page 44. Thus, p.warning and p[class~="warning"] are
Attribute Selectors
|
49
equivalent when applied to HTML documents. Here’s an example that is an HTML
version of the “PlanetML” markup seen earlier:
MercuryVenusEarth
To italicize all elements with the word barren in their class attribute, you write:
span[class~="barren"] {font-style: italic;}
This rule’s selector will match the first two elements in the example markup and thus
italicize their text, as shown in Figure 2-11. This is the same result we would expect
from writing span.barren {font-style: italic;}.
Figure 2-11. Selecting elements based on portions of attribute values
So why bother with the tilde-equals attribute selector in HTML? Because it can be
used for any attribute, not just class. For example, you might have a document that
contains a number of images, only some of which are figures. You can use a partialmatch value attribute selector aimed at the title text to select only those figures:
img[title~="Figure"] {border: 1px solid gray;}
This rule selects any image whose title text contains the word Figure. Therefore, as
long as all your figures have title text that looks something like “Figure 4. A baldheaded elder statesman,” this rule will match those images. For that matter, the selec‐
tor img[title~="Figure"] will also match a title attribute with the value “How to
Figure Out Who’s in Charge.” Any image that does not have a title attribute, or
whose title value doesn’t contain the word “Figure,” won’t be matched.
Matching a substring within an attribute value
Sometimes you want to select elements based on a portion of their attribute values,
but the values in question aren’t space-separated lists of words. In these cases, you can
use the form [att*="val"] to match substrings that appear anywhere inside the
attribute values. For example, the following CSS matches any span element whose
class attribute contains the substring cloud, so both “cloudy” planets are matched, as
shown in Figure 2-12:
span[class*="cloud"] {font-style: italic;}
MercuryVenusEarth
50
|
Chapter 2: Selectors
Figure 2-12. Selecting elements based on substrings within attribute values
As you can imagine, there are many useful applications for this particular capability.
For example, suppose you wanted to specially style any links to the O’Reilly website.
Instead of classing them all and writing styles based on that class, you could instead
write the following rule:
a[href*="oreilly.com"] {font-weight: bold;}
You aren’t confined to the class and href attributes. Any attribute is up for grabs
here: title, alt, src, id…if the attribute has a value, you can style based on a sub‐
string within that value. The following rule draws attention to any image with the
string “space” in its source URL:
img[src*="space"] {border: 5px solid red;}
Similarly, the following rule draws attention to input elements that have a title tells
the user what to, and any other input whose title contains the substring “format” in its
title:
input[title*="format"] {background-color: #dedede;}
A common use for the general substring attribute selector is to match a section of a
class in pattern library class names. Elaborating on the last example, we can target any
class name that starts with "btn" followed by a dash, and that contains the substring
“arrow” preceded by a dash:
*[class|="btn"][class*="-arrow"]:after { content: "▼";}
The matches are exact: if you include whitespace in your selector, then whitespace
must also be present in an attribute’s value. The attribute names and values must be
case-sensitive only if the underlying document language requires case sensitivity.
Class names, titles, URLs, and ID values are all case-sensitive, but HTML attribute
keyterm values, such as input types, are not:
input[type="CHeckBoX"] {margin-right: 10px;}
Matching a substring at the beginning of an attribute value
In cases where you want to select elements based on a substring at the beginning of
an attribute value, then the attribute selector pattern [att^="val"] is what you’re
Attribute Selectors
|
51
seeking. This can be particularly useful in a situation where you want to style types of
links differently, as illustrated in Figure 2-13.
a[href^="https:"] {font-weight: bold;}
a[href^="mailto:"] {font-style: italic;}
Figure 2-13. Selecting elements based on substrings that begin attribute values
Another use case is when you want to style all images in an article that are also fig‐
ures, as in the figures you see throughout this text. Assuming that the alt text of each
figure begins with text in the pattern “Figure 5”—which is an entirely reasonable
assumption in this case—then you can select only those images as follows:
img[alt^="Figure"] {border: 2px solid gray;
display: block; margin: 2em auto;}
The potential drawback here is that any img element whose alt starts with “Figure”
will be selected, whether or not it’s meant to be an illustrative figure. The likeliness of
that occurring depends on the document in question.
Another use case is selecting all of the calendar events that occur on Mondays. In this
case, let’s assume all of the events have a title attribute containing a date in the for‐
mat “Monday, March 5th, 2012.” Selecting them all is a simple matter of
[title^="Monday"].
Matching a substring at the end of an attribute value
The mirror image of beginning-substring matching is ending-substring matching,
which is accomplished using the [att$="val"] pattern. A very common use for this
capability is to style links based on the kind of resource they target, such as separate
styles for PDF documents, as illustrated in Figure 2-14.
a[href$=".pdf"] {font-weight: bold;}
Figure 2-14. Selecting elements based on substrings that end attribute values
52
|
Chapter 2: Selectors
Similarly, you could (for whatever reason) select images based on their image format:
img[src$=".gif"] {...}
img[src$=".jpg"] {...}
img[src$=".png"] {...}
To continue the calendar example from the previous section, it would be possible to
select all of the events occurring within a given year using a selector like [title
$="2015"].
You may have noticed that I’ve quoted all the attribute values in the
attribute selectors. Quoting is required if the value includes any
special characters, begins with a dash or digit, or is otherwise inva‐
lid as an identifier and needs to be quoted as a string. To be safe, I
recommend always quoting attribute values in attribute selectors,
even though it is only required to makes strings out of invalid iden‐
tifiers.
The Case Insensitivity Identifier
CSS Selectors level 4 introduces a case-insensitivity option to attribute selectors.
Including an i before the closing bracket will allow the selector to match attribute
values case-insensitively, regardless of document language rules.
For example, suppose you want to select all links to PDF documents, but you don’t
know if they’ll end in .pdf, .PDF, or even .Pdf. Here’s how:
a[href$='.PDF' i]
Adding that humble little i means the selector will match any a element whose href
attribute’s value ends in .pdf, regardless of the capitalization of the letters P, D, and F.
This case-insensitivity option is available for all attribute selectors we’ve covered.
Note, however, that this only applies to the values in the attribute selectors. It does not
enforce case insensitivity on the attribute names themselves. Thus, in a case-sensitive
language, planet[type*="rock" i] will match all of the following:
MercuryVenusEarth
It will not match the following element, because the attribute TYPE isn’t matched by
type:
Mars
Again, that’s in langauges that enforce case sensitivity in the element and attribute
syntax. XHTML was one such. In languages that are case-insensitive, like HTML5,
this isn’t an issue.
Attribute Selectors
|
53
As of late 2017, Opera Mini, the Android browser, and Edge did
not support this capability.
Using Document Structure
CSS is powerful because it uses the structure of documents to determine appropriate
styles and how to apply them. Yet structure plays a much larger role in the way styles
are applied to a document. Let’s take a moment to discuss structure before moving on
to more powerful forms of selection.
Understanding the Parent-Child Relationship
To understand the relationship between selectors and documents, we need to once
again examine how documents are structured. Consider this very simple HTML
document:
Meerkat Central
Meerkat Central
Welcome to Meerkat Central, the best meerkat web site
on the entire Internet!
54
|
Chapter 2: Selectors
Much of the power of CSS is based on the parent-child relationship of elements.
HTML documents (actually, most structured documents of any kind) are based on a
hierarchy of elements, which is visible in the “tree” view of the document (see
Figure 2-15). In this hierarchy, each element fits somewhere into the overall structure
of the document. Every element in the document is either the parent or the child of
another element, and it’s often both.
Figure 2-15. A document tree structure
An element is said to be the parent of another element if it appears directly above that
element in the document hierarchy. For example, in Figure 2-15, the first p element is
parent to the em and strong elements, while strong is parent to an anchor (a) ele‐
ment, which is itself parent to another em element. Conversely, an element is the child
of another element if it is directly beneath the other element. Thus, the anchor ele‐
ment in Figure 2-15 is a child of the strong element, which is in turn child to the p
element, which is itself child to the body, and so on.
The terms “parent” and “child” are specific applications of the terms ancestor and
descendant. There is a difference between them: in the tree view, if an element is
exactly one level above or below another, then they have a parent-child relationship.
If the path from one element to another is traced through two or more levels, the ele‐
ments have an ancestor-descendant relationship, but not a parent-child relationship.
(A child is also a descendant, and a parent is also an ancestor.) In Figure 2-15, the
first ul element is parent to two li elements, but the first ul is also the ancestor of
Using Document Structure
|
55
every element descended from its li element, all the way down to the most deeply
nested li elements.
Also, in Figure 2-15, there is an anchor that is a child of strong, but also a descendant
of p, body, and html elements. The body element is an ancestor of everything that the
browser will display by default, and the html element is ancestor to the entire docu‐
ment. For this reason, in an HTML or XHTML document, the html element is also
called the root element.
Descendant Selectors
The first benefit of understanding this model is the ability to define descendant selec‐
tors (also known as contextual selectors). Defining descendant selectors is the act of
creating rules that operate in certain structural circumstances but not others. As an
example, let’s say you want to style only those em elements that are descended from h1
elements. You could put a class attribute on every em element found within an h1,
but that’s almost as time-consuming as using the font tag. It’s far more efficient to
declare rules that match only em elements that are found inside h1 elements.
To do so, write the following:
h1 em {color: gray;}
This rule will make gray any text in an em element that is the descendant of an h1
element. Other em text, such as that found in a paragraph or a block quote, will not be
selected by this rule. Figure 2-16 makes this clear.
Figure 2-16. Selecting an element based on its context
In a descendant selector, the selector side of a rule is composed of two or more spaceseparated selectors. The space between the selectors is an example of a combinator.
Each space combinator can be translated as “found within,” “which is part of,” or “that
is a descendant of,” but only if you read the selector right to left. Thus, h1 em can be
translated as, “Any em element that is a descendant of an h1 element.” (To read the
selector left to right, you might phrase it something like, “Any h1 that contains an em
will have the following styles applied to the em.”)
You aren’t limited to two selectors. For example:
ul ol ul em {color: gray;}
In this case, as Figure 2-17 shows, any emphasized text that is part of an unordered
list that is part of an ordered list that is itself part of an unordered list (yes, this is
correct) will be gray. This is obviously a very specific selection criterion.
56
|
Chapter 2: Selectors
Figure 2-17. A very specific descendant selector
Descendant selectors can be extremely powerful. They make possible what could
never be done in HTML—at least not without oodles of font tags. Let’s consider a
common example. Assume you have a document with a sidebar and a main area. The
sidebar has a blue background, the main area has a white background, and both areas
include lists of links. You can’t set all links to be blue because they’d be impossible to
read in the sidebar.
The solution: descendant selectors. In this case, you give the element that contains
your sidebar a class of sidebar and enclose the main area in a main element. Then,
you write styles like this:
.sidebar {background: blue;}
main {background: white;}
.sidebar a:link {color: white;}
main a:link {color: blue;}
Figure 2-18 shows the result.
Figure 2-18. Using descendant selectors to apply different styles to the same type of ele‐
ment
:link refers to links to resources that haven’t been visited. We’ll
talk about it in detail in “Hyperlink pseudo-classes” on page 77.
Using Document Structure
|
57
Here’s another example: let’s say that you want gray to be the text color of any b (bold‐
face) element that is part of a blockquote and for any bold text that is found in a nor‐
mal paragraph:
blockquote b, p b {color: gray;}
The result is that the text within b elements that are descended from paragraphs or
block quotes will be gray.
One overlooked aspect of descendant selectors is that the degree of separation
between two elements can be practically infinite. For example, if you write ul em, that
syntax will select any em element descended from a ul element, no matter how deeply
nested the em may be. Thus, ul em would select the em element in the following
markup:
List item 1
List item 1-1
List item 1-2
List item 1-3
List item 1-3-1
List item 1-3-2
List item 1-3-3
List item 1-4
A more subtle aspect of descendant selectors is that they have no notion of element
proximity. In other words, the closeness of two elements within the document tree
has no bearing on whether a rule applies or not. This is important when it comes to
specificity (which we’ll cover later on) and when considering rules that might appear
to cancel each other out.
For example, consider the following (which contains a selector type we’ll discuss in
the upcoming section, “The Negation Pseudo-Class” on page 89):
div:not(.help) span {color: gray;}
div.help span {color: red;}
This text contains a span element within.
58
|
Chapter 2: Selectors
What the CSS says, in effect, is “any span inside a div that doesn’t have a class con‐
taining the word help should be gray” in the first rule, and “any span inside a div
whose class contains the word help” in the second rule. In the given markup frag‐
ment, both rules apply to the span shown.
Because the two rules have equal weight and the “red” rule is written last, it wins out
and the span is red. The fact that the div class="aside" is “closer to” the span than
the div class="help" is irrelevant. Again: descendant selectors have no notion of
element proximity. Both rules match, only one color can be applied, and due to the
way CSS works, red is the winner here. (We’ll discuss why in the next chapter.)
Selecting Children
In some cases, you don’t want to select an arbitrarily descended element. Rather, you
want to narrow your range to select an element that is a child of another element. You
might, for example, want to select a strong element only if it is a child (as opposed to
any level of descendant) of an h1 element. To do this, you use the child combinator,
which is the greater-than symbol (>):
h1 > strong {color: red;}
This rule will make red the strong element shown in the first h1, but not the second:
This is very important.
This is really very important.
Read right to left, the selector h1 > strong translates as, “Selects any strong element
that is a direct child of an h1 element.” The child combinator can be optionally sur‐
rounded by whitespace. Thus, h1 > strong, h1> strong, and h1>strong are all
equivalent. You can use or omit whitespace as you wish.
When viewing the document as a tree structure, it’s easy to see that a child selector
restricts its matches to elements that are directly connected in the tree. Figure 2-19
shows part of a document tree.
Figure 2-19. A document tree fragment
Using Document Structure
|
59
In this tree fragment, you can pick out parent-child relationships. For example, the a
element is parent to the strong, but it is child to the p element. You could match ele‐
ments in this fragment with the selectors p > a and a > strong, but not p > strong,
since the strong is a descendant of the p but not its child.
You can also combine descendant and child combinations in the same selector. Thus,
table.summary td > p will select any p element that is a child of a td element that is
itself descended from a table element that has a class attribute containing the word
summary.
Selecting Adjacent Sibling Elements
Let’s say you want to style the paragraph immediately after a heading, or give a special
margin to a list that immediately follows a paragraph. To select an element that
immediately follows another element with the same parent, you use the adjacentsibling combinator, represented as a plus symbol (+). As with the child combinator,
the symbol can be surrounded by whitespace, or not, at the author’s discretion.
To remove the top margin from a paragraph immediately following an h1 element,
write:
h1 + p {margin-top: 0;}
The selector is read as, “Selects any p element that immediately follows an h1 element
that shares a parent with the p element.”
To visualize how this selector works, it is easiest to once again consider a fragment of
a document tree, shown in Figure 2-20.
Figure 2-20. Another document tree fragment
In this fragment, a pair of lists descends from a div element, one ordered and the
other not, each containing three list items. Each list is an adjacent sibling, and the list
items themselves are also adjacent siblings. However, the list items from the first list
are not siblings of the second, since the two sets of list items do not share the same
parent element. (At best, they’re cousins, and CSS has no cousin selector.)
60
|
Chapter 2: Selectors
Remember that you can select the second of two adjacent siblings only with a single
combinator. Thus, if you write li + li {font-weight: bold;}, only the second and
third items in each list will be boldfaced. The first list items will be unaffected, as
illustrated in Figure 2-21.
Figure 2-21. Selecting adjacent siblings
To work properly, CSS requires that the two elements appear in “source order.” In our
example, an ol element is followed by a ul element. This allows us to select the sec‐
ond element with ol + ul, but we cannot select the first using the same syntax. For
ul + ol to match, an ordered list must immediately follow an unordered list.
Keep in mind that text content between two elements does not prevent the adjacentsibling combinator from working. Consider this markup fragment, whose tree view
would be the same as that shown in Figure 2-19:
List item 1
List item 1
List item 1
This is some text that is part of the 'div'.
A list item
Another list item
Yet another list item
Even though there is text between the two lists, we can still match the second list with
the selector ol + ul. That’s because the intervening text is not contained with a sib‐
ling element, but is instead part of the parent div. If we wrapped that text in a para‐
graph element, it would then prevent ol + ul from matching the second list. Instead,
we might have to write something like ol + p + ul.
Using Document Structure
|
61
As the following example illustrates, the adjacent-sibling combinator can be used in
conjunction with other combinators:
html > body table + ul{margin-top: 1.5em;}
The selector translates as, “Selects any ul element that immediately follows a sibling
table element that is descended from a body element that is itself a child of an html
element.”
As with all combinators, you can place the adjacent-sibling combinator in a more
complex setting, such as div#content h1 + div ol. That selector is read as, “Selects
any ol element that is descended from a div when the div is the adjacent sibling of an
h1 which is itself descended from a div whose id attribute has a value of content.”
Selecting Following Siblings
Selectors Level 3 introduced a new sibling combinator called the general sibling com‐
binator. This lets you select any element that follows another element when both ele‐
ments share the same parent, represented using the tilde (~) combinator.
As an example, to italicize any ol that follows an h2 and also shares a parent with the
h2, you’d write h2 ~ol {font-style: italic;}. The two elements do not have to be
adjacent siblings, although they can be adjacent and still match this rule. The result of
applying this rule to the following markup is shown in Figure 2-22:
Subheadings
It is the case that not every heading can be a main heading. Some headings
must be subheadings. Examples include:
Headings that are less important
Headings that are subsidiary to more important headlines
Headings that like to be dominated
Let's restate that for the record:
Headings that are less important
Headings that are subsidiary to more important headlines
Headings that like to be dominated
As you can see, both ordered lists are italicized. That’s because both of them are ol
elements that follow an h2 with which they share a parent (the div).
62
|
Chapter 2: Selectors
Figure 2-22. Selecting following siblings
Pseudo-Class Selectors
Things get really interesting with pseudo-class selectors. These selectors let you assign
styles to what are, in effect, phantom classes that are inferred by the state of certain
elements, or markup patterns within the document, or even by the state of the docu‐
ment itself.
The phrase “phantom classes” might seem a little odd, but it really is the best way to
think of how pseudo-classes work. For example, suppose you wanted to highlight
every other row of a data table. You could do that by marking up every other row
something like class="even" and then writing CSS to highlight rows with that class
—or (as we’ll soon see) you could use a pseudo-class selector to achieve the same
effect, and through very similar means.
Combining Pseudo-Classes
Before we start, a word about chaining. CSS makes it possible to combine (“chain”)
pseudo-classes together. For example, you can make unvisited links red when they’re
hovered and visited links maroon when they’re hovered:
a:link:hover {color: red;}
a:visited:hover {color: maroon;}
The order you specify doesn’t actually matter; you could also write a:hover:link to
the same effect as a:link:hover. It’s also possible to assign separate hover styles to
unvisited and visited links that are in another language—for example, German:
a:link:hover:lang(de) {color: gray;}
a:visited:hover:lang(de) {color: silver;}
Pseudo-Class Selectors
|
63
Be careful not to combine mutually exclusive pseudo-classes. For example, a link can‐
not be both visited and unvisited, so a:link:visited doesn’t make any sense and will
never match anything.
Structural Pseudo-Classes
The majority of pseudo-classes are structural in nature; that is, they refer to the
markup structure of the document. Most of them depend on patterns within the
markup, such as choosing every third paragraph, but others allow you to address spe‐
cific types of elements. All pseudo-classes, without exception, are a word preceded by
a single colon (:), and they can appear anywhere in a selector.
Before we get started, there’s an aspect of pseudo-classes that needs to be made
explicit here: pseudo-classes always refer to the element to which they’re attached,
and no other. Seems like a weirdly obvious thing to say, right? The reason to make it
explicit is that for a few of the structural pseudo-classes, it’s a common error to think
they are descriptors that refer to descendant elements.
To illustrate this, I’d like to share a personal anecdote. When my first child was born
in 2003, I announced it online (like you do). A number of people responded with
congratulations and CSS jokes, chief among them the selector #ericmeyer:firstchild. The problem there is that selector would select me, not my daughter, and only
if I were the first child of my parents (which, as it happens, I am). To properly select
my first child, that selector would need to be #ericmeyer > :first-child.
The confusion is understandable, which is why I’m addressing it here. Reminders will
be found throughout the following sections. Just always keep in mind that the effect
of pseudo-classes is to apply a sort of a “phantom class” to the element to which
they’re attached, and you should be OK.
Selecting the root element
This is the quintessence of structural simplicity: the pseudo-class :root selects the
root element of the document. In HTML, this is always the html element. The real
benefit of this selector is found when writing stylesheets for XML languages, where
the root element may be different in every language—for example, in RSS 2.0 it’s the
rss element—or even when you have more than one possible root element within a
single language (though not a single document!).
Here’s an example of styling the root element in HTML, as illustrated in Figure 2-23:
:root {border: 10px dotted gray;}
body {border: 10px solid black;}
64
|
Chapter 2: Selectors
Figure 2-23. Styling the root element
In HTML documents, you can always select the html element directly, without having
to use the :root pseudo-class. There is a difference between the two selectors in
terms of specificity, which we’ll cover in Chapter 3.
Selecting empty elements
With the pseudo-class :empty, you can select any element that has no children of any
kind, including text nodes, which covers both text and whitespace. This can be useful
in suppressing elements that a CMS has generated without filling in any actual con‐
tent. Thus, p:empty {display: none;} would prevent the display of any empty para‐
graphs.
Note that in order to be matched, an element must be, from a parsing perspective,
truly empty—no whitespace, visible content, or descendant elements. Of the follow‐
ing elements, only the first and last would be matched by p:empty:
The second and third paragraphs are not matched by :empty because they are not
empty: they contain, respectively, a single space and a single newline character. Both
are considered text nodes, and thus prevent a state of emptiness. The last paragraph
matches because comments are not considered content, not even whitespace. But put
even one space or newline to either side of that comment, and p:empty would fail to
match.
You might be tempted to just style all empty elements with something like *:empty
{display: none;}, but there’s a hidden catch: :empty matches HTML’s empty ele‐
ments, like img and input. It could even match textarea, unless you insert some
default text into the textarea element. Thus, in terms of matching elements, img and
img:empty are effectively the same. (They are different in terms of specificity, which
we’ll cover in the next chapter.)
Pseudo-Class Selectors
|
65
As of late 2017, :empty is unique in that it’s the only CSS selector
that takes text nodes into consideration when determining
matches. Every other selector type in Selectors Level 3 considers
only elements, and ignores text nodes entirely—recall, for example,
the sibling combinators.
Selecting unique children
If you’ve ever wanted to select all the images that are wrapped by a hyperlink element,
the :only-child pseudo-class is for you. It selects elements when they are the only
child element of another element. So let’s say you want to add a border to any image
that’s the only child of another element. You’d write:
img:only-child {border: 1px solid black;}
This would match any image that meets those criteria. Therefore, if you had a para‐
graph which contained an image and no other child elements, the image would be
selected regardless of all the text surrounding it. If what you’re really after is images
that are sole children and found inside hyperlinks, then you just modify the selector
like so (which is illustrated in Figure 2-24):
a[href] img:only-child {border: 2px solid black;}
The W3CThe W3C
Figure 2-24. Selecting images that are only children inside links
There are two things to remember about :only-child. The first is that you always
apply it to the element you want to be an only child, not to the parent element, as
explained earlier. And that brings up the second thing to remember, which is that
when you use :only-child in a descendant selector, you aren’t restricting the ele‐
ments listed to a parent-child relationship.
To go back to the hyperlinked-image example, a[href] img:only-child matches
any image that is an only child and is descended from an a element, not is a child of
an a element. To match, the element image must be the only child of its direct parent,
66
| Chapter 2: Selectors
and a descendant of a link, but that parent can itself be a descendant of that link.
Therefore, all three of the images here would be matched, as shown in Figure 2-25:
a[href] img:only-child {border: 5px solid black;}
A link to the
web site
Figure 2-25. Selecting images that are only children inside links
In each case, the image is the only child element of its parent, and it is also descended
from an a element. Thus, all three images are matched by the rule shown. If you
wanted to restrict the rule so that it matched images that were the only children of a
elements, then you’d just add the child combinator to yield a[href] > img:onlychild. With that change, only the first of the three images shown in Figure 2-25
would be matched.
That’s all great, but what if you want to match images that are the only images inside
hyperlinks, but there are other elements in there with them? Consider the following:
•
In this case, we have an a element that has two children: b and img. That image, no
longer being the only child of its parent (the hyperlink), can never be matched
using :only-child. However, it can be matched using :only-of-type. This is illus‐
trated in Figure 2-26:
a[href] img:only-of-type {border: 5px solid black;}
••
Figure 2-26. Selecting images that are the only sibling of their type
The difference is that :only-of-type will match any element that is the only of its
type among all its siblings, whereas :only-child will only match if an element has no
siblings at all.
Pseudo-Class Selectors
|
67
This can be very useful in cases such as selecting images within paragraphs without
having to worry about the presence of hyperlinks or other inline elements:
p > img:only-of-type {float: right; margin: 20px;}
As long as there aren’t multiple images that are children of the same paragraph, then
the image will be floated. You could also use this pseudo-class to apply extra styles to
an h2 when it’s the only one in a section of a document, like this:
section > h2 {margin: 1em 0 0.33em; font-size: 1.8rem; border-bottom: 1px solid
gray;}
section > h2:only-of-type {font-size: 2.4rem;}
Given those rules, any section that has only one child h2 will have it appear larger
than usual. If there are two or more h2 children to a section, neither of them will be
larger than the other. The presence of other children—whether they are other head‐
ing levels, paragraphs, tables, paragraphs, lists, and so on—will not interfere with
matching.
There’s one more thing to make clear, which is that :only-of-type refers to elements
and nothing else. Consider the following:
p.unique:only-of-type {color: red;}
This paragraph has a 'unique' class.
This paragraph doesn't have a class at all.
In this case, neither of the paragraphs will be selected. Why not? Because there are
two paragraphs that are descendants of the div, so neither of them can be the only
one of their type.
The class name is irrelevant here. We’re fooled into thinking that “type” is a generic
description, because of how we parse language. Type, in the way :only-of-type
means it, refers only to the element type. Thus, p.unique:only-of-type means
“select any p element whose class attribute contains the word unique when the p
element is the only p element among its siblings.” It does not mean “select any p ele‐
ment whose class attribute contains the word unique when it’s the only sibling para‐
graph to meet that criterion.”
Selecting first and last children
It’s pretty common to want to apply special styling to the first or last child of an ele‐
ment. A common example is styling a bunch of navigation links in a tab bar, and
wanting to put some special visual touches on the first or last tab (or both). In the
past, this was done by applying special classes to those elements. Now we have
pseudo-classes to carry the load for us.
68
| Chapter 2: Selectors
The pseudo-class :first-child is used to select elements that are the first children of
other elements. Consider the following markup:
These are the necessary steps:
Insert key
Turn key clockwise
Push accelerator
Do not push the brake at the same time as the accelerator.
In this example, the elements that are first children are the first p, the first li, and the
strong and em elements, which are all the first children of their respective parents.
Given the following two rules:
p:first-child {font-weight: bold;}
li:first-child {text-transform: uppercase;}
we get the result shown in Figure 2-27.
Figure 2-27. Styling first children
The first rule boldfaces any p element that is the first child of another element. The
second rule uppercases any li element that is the first child of another element
(which, in HTML, must be either an ol or ul element).
As has been mentioned, the most common error is assuming that a selector like
p:first-child will select the first child of a p element. Remember the nature of
pseudo-classes, which is to attach a sort of phantom class to the element associated
with the pseudo-class. If you were to add actual classes to the markup, it would look
like this:
These are the necessary steps:
Insert key
Turn key clockwise
Push accelerator
Pseudo-Class Selectors
|
69
Do not push the brake at the same time as the
accelerator.
Therefore, if you want to select those em elements that are the first child of another
element, you write em:first-child.
The mirror image of :first-child is :last-child. If we take the previous example
and just change the pseudo-classes, we get the result shown in Figure 2-28.
p:last-child {font-weight: bold;}
li:last-child {text-transform: uppercase;}
These are the necessary steps:
Insert key
Turn key clockwise
Push accelerator
Do not push the brake at the same time as the accelerator.
Figure 2-28. Styling last children
The first rule boldfaces any p element that is the last child of another element. The
second rule uppercases any li element that is the last child of another element. If you
wanted to select the em element inside that last paragraph, you could use the selector
p:last-child em, which selects any em element that descends from a p element that
is itself the last child of another element.
Interestingly, you can combine these two pseudo-classes to create a version of :onlychild. The following two rules will select the same elements:
p:only-child {color: red;}
p:first-child:last-child {background-color: red;}
Either way, we get paragraphs with red foreground and background colors (not a
good idea, clearly).
70
|
Chapter 2: Selectors
Selecting first and last of a type
In a manner similar to selecting the first and last children of an element, you can
select the first or last of a type of element within another element. This permits things
like selecting the first table inside a given element, regardless of whatever elements
come before it.
table:first-of-type {border-top: 2px solid gray;}
Note that this does not apply to the entire document; that is, the rule shown will not
select the first table in the document and skip all the others. It will instead select the
first table element within each element that contains one, and skip any sibling table
elements that come after the first. Thus, given the document structure shown in
Figure 2-29, the circled nodes are the ones that are selected.
Figure 2-29. Selecting first-of-type tables
Within the context of tables, a useful way to select the first data cell within a row
regardless of whether a header cell comes before it in the row is as follows:
td:first-of-type {border-left: 1px solid red;}
That would select the first data cell in each of the following table rows:
Count
7
6
11
Q
X
-
Compare that to the effects of td:first-child, which would select the first td ele‐
ment in the second row, but not in the first row.
The flip side is :last-of-type, which selects the last instance of a given type from
amongst its sibling elements. In a way, it’s just like :first-of-type except you start
with the last element in a group of siblings and walk backward toward the first ele‐
ment until you reach an instance of the type. Given the document structure shown in
Figure 2-30, the circled nodes are the ones selected by table:last-of-type.
Pseudo-Class Selectors
|
71
Figure 2-30. Selecting last-of-type tables
As was noted with :only-of-type, remember that you are selecting elements of a
type from among their sibling elements; thus, every set of siblings is considered sepa‐
rately. In other words, you are not selecting the first (or last) of all the elements of a
type within the entire document as a single group. Each set of elements that share a
parent is its own group, and you can select the first (or last) of a type within each
group.
Similar to what was noted in the previous section, you can combine these two
pseudo-classes to create a version of :only-of-type. The following two rules will
select the same elements:
table:only-of-type{color: red;}
table:first-of-type:last-of-type {background: red;}
Selecting every nth child
If you can select elements that are the first, last, or only children of other elements,
how about every third child? All even children? Only the ninth child? Rather than
define a literally infinite number of named pseudo-classes, CSS has the :nth-child()
pseudo-class. By filling integers or even simple algebraic expressions into the paren‐
theses, you can select any arbitrarily numbered child element you like.
Let’s start with the :nth-child() equivalent of :first-child, which is :nthchild(1). In the following example, the selected elements will be the first paragraph
and the first list item.
p:nth-child(1) {font-weight: bold;}
li:nth-child(1) {text-transform: uppercase;}
These are the necessary steps:
Insert key
Turn key clockwise
Push accelerator
Do not push the brake at the same time as the accelerator.
72
|
Chapter 2: Selectors
If we change the numbers from 1 to 2, however, then no paragraphs will be selected,
and the middle (or second) list item will be selected, as illustrated in Figure 2-31:
p:nth-child(2) {font-weight: bold;}
li:nth-child(2) {text-transform: uppercase;}
Figure 2-31. Styling second children
You can insert any integer you choose; if you have a use case for selecting any ordered
list that is the 93rd child element of its parent, then ol:nth-child(93) is ready to
serve. This will match the 93rd child of any parent as long as that child is an ordered
list. (This does not mean the 93rd ordered list among its siblings; see the next section
for that.)
More powerfully, you can use simple algebraic expressions in the form a n + b or a n
− b to define recurring instances, where a and b are integers and n is present as itself.
Furthermore, the + b or − b part is optional and thus can be dropped if it isn’t needed.
Let’s suppose we want to select every third list item in an unordered list, starting with
the first. The following makes that possible, selecting the first and fourth items, as
shown in Figure 2-32.
ul > li:nth-child(3n + 1) {text-transform: uppercase;}
Figure 2-32. Styling every third list item
The way this works is that n represents the series 0, 1, 2, 3, 4, and on into infinity. The
browser then solves for 3 n + 1, yielding 1, 4, 7, 10, 13, and so on. Were we to drop
the +1, thus leaving us with simply 3n, the results would be 0, 3, 6, 9, 12, and so on.
Pseudo-Class Selectors
|
73
Since there is no zeroth list item—all element counting starts with one, to the likely
chagrin of array-slingers everywhere—the first list item selected by this expression
would be the third list item in the list.
Given that element counting starts with one, it’s a minor trick to deduce that :nthchild(2n) will select even-numbered children, and either :nth-child(2n+1)
or :nth-child(2n-1) will select odd-numbered children. You can commit that to
memory, or you can use the two special keywords that :nth-child() accepts: even
and odd. Want to highlight every other row of a table, starting with the first? Here’s
how you do it, with the results shown in Figure 2-33:
tr:nth-child(odd) {background: silver;}
Figure 2-33. Styling every other table row
Anything more complex than every-other-element requires an an + b expression.
Note that when you want to use a negative number for b, you have to remove the +
sign or else the selector will fail entirely. Of the following two rules, only the first will
do anything. The second will be dropped by the parser and ignored:
tr:nth-child(4n - 2) {background: silver;}
tr:nth-child(3n + −2) {background: red;}
If you want to select every row starting with the ninth, you can use either of the fol‐
lowing. They are similar in that they will select all rows starting with the ninth, but
the latter one has greater specificity, which we discuss in Chapter 3:
tr:nth-child(n + 9) {background: silver;}
tr:nth-child(8) ~ tr {background: silver;}
As you might expect, there is a corresponding pseudo-class in :nth-last-child().
This lets you do the same thing as :nth-child(), except with :nth-last-child()
you start from the last element in a list of siblings and count backward toward the
74
|
Chapter 2: Selectors
beginning. If you’re intent on highlighting every other table row and making sure the
very last row is one of the rows in the highlighting pattern, either one of these will
work for you:
tr:nth-last-child(odd) {background: silver;}
tr:nth-last-child(2n+1) {background: silver;} /* equivalent */
If the DOM is updated to add or remove table rows, there is no need to add or
remove classes. By using structural selectors, these selectors will always match the
odd rows of the updated DOM.
Any element can be matched using both :nth-child() and :nth-last-child() if it
fits the criteria. Consider these rules, the results of which are shown in Figure 2-34:
li:nth-child(3n + 3) {border-left: 5px solid black;}
li:nth-last-child(4n - 1) {border-right: 5px solid black; background: silver;}
Figure 2-34. Combining patterns of :nth-child() and :nth-last-child()
It’s also the case that you can string these two pseudo-classes together as :nthchild(1):nth-last-child(1), thus creating a more verbose restatement of :onlychild. There’s no real reason to do so other than to create a selector with a higher
specificity, but the option is there.
You can use CSS to determine how many list items are in a list, and style them
accordingly:
li:only-child {width: 100%;}
li:nth-child(1):nth-last-child(2),
li:nth-child(2):nth-last-child(1) {width: 50%;}
li:nth-child(1):nth-last-child(3),
li:nth-child(1):nth-last-child(3) ~ li {width: 33.33%;}
li:nth-child(1):nth-last-child(4),
li:nth-child(1):nth-last-child(4) ~ li {width: 25%;}
Pseudo-Class Selectors
|
75
In these examples, if a list item is the only list item, then the width is 100%. If a list
item is the first item and also the second-from-the-last item, that means there are two
items, and the width is 50%. If an item is the first item and also the third from the last
item, then we make it, and the two sibling list items following it, 33% wide. Similarly,
if a list item is the first item and also the fourth from the last item, it means that there
are exactly four items, so we make it, and its three siblings, 25% of the width.
Selecting every nth of a type
In what’s probably become a familiar pattern, the :nth-child() and :nth-lastchild() pseudo-classes have analogues in :nth-of-type() and :nth-last-oftype(). You can, for example, select every other hyperlink that’s a child of any given
paragraph, starting with the second, using p > a:nth-of-type(even). This will
ignore all other elements (spans, strongs, etc.) and consider only the links, as
demonstrated in Figure 2-35:
p > a:nth-of-type(even) {background: blue; color: white;}
Figure 2-35. Selecting the even-numbered links
If you want to work from the last hyperlink backward, then you’d use p > a:nthlast-of-type(even).
As before, these select elements of a type from among their sibling elements, not from
among all the elements of a type within the entire document as a single group. Each
element has its own list of siblings, and selections happen within each group.
As you might expect, you can string these two together as :nth-of-type(1):nthlast-of-type(1) to restate :only-of-type, only with higher specificity. (We will
explain specificity in Chapter 3, I promise.)
Dynamic Pseudo-Classes
Beyond the structural pseudo-classes, there are a set of pseudo-classes that relate to
structure but can change based on changes made to the page after it’s been rendered.
In other words, the styles are applied to pieces of a document based on something in
76
| Chapter 2: Selectors
addition to the structure of the document, and in a way that cannot be precisely
deduced simply by studying the document’s markup.
It may sound like we’re applying styles at random, but not so. Instead, we’re applying
styles based on somewhat ephemeral conditions that can’t be predicted in advance.
Nevertheless, the circumstances under which the styles will appear are, in fact, welldefined. Think of it this way: during a sporting event, whenever the home team
scores, the crowd will cheer. You don’t know exactly when during a game the team
will score, but when it does, the crowd will cheer, just as predicted. The fact that you
can’t predict the exact moment of the cheer doesn’t make it any less expected.
Consider the anchor element (a), which (in HTML and related languages) establishes
a link from one document to another. Anchors are always anchors, but some anchors
refer to pages that have already been visited, while others refer to pages that have yet
to be visited. You can’t tell the difference by simply looking at the HTML markup,
because in the markup, all anchors look the same. The only way to tell which links
have been visited is by comparing the links in a document to the user’s browser his‐
tory. So there are actually two basic types of links: visited and unvisited.
Hyperlink pseudo-classes
CSS2.1 defines two pseudo-classes that apply only to hyperlinks. In HTML, these are
any a elements with an href attribute; in XML languages, they’re any elements that
act as links to another resource. Table 2-2 describes the pseudo-classes you can apply
to them.
Table 2-2. Link pseudo-classes
Name
Description
:link
Refers to any anchor that is a hyperlink (i.e., has an href attribute) and points to an address that has not
been visited.
:visited Refers to any anchor that is a hyperlink to an already visited address. For security reasons, the styles that can
be applied to visited links are severely limited; see sidebar “Visited Links and Privacy” on page 79 for details.
The first of the pseudo-classes in Table 2-2 may seem a bit redundant. After all, if an
anchor hasn’t been visited, then it must be unvisited, right? If that’s the case, all we
should need is the following:
a {color: blue;}
a:visited {color: red;}
Although this format seems reasonable, it’s actually not quite enough. The first of the
rules shown here applies not only to unvisited links, but also to placeholder links
such as this one:
4. The Lives of Meerkats
Pseudo-Class Selectors
|
77
The resulting text would be blue because the a element will match the rule a {color :
blue;}. Therefore, to avoid applying your link styles to placeholders, use the :link
and :visited pseudo-classes:
a:link {color: blue;}
a:visited {color: red;}
/* unvisited links are blue */
/* visited links are red */
This is a good place to revisit attribute and class selectors and show how they can be
combined with pseudo-classes. For example, let’s say you want to change the color of
links that point outside your own site. In most circumstances we can use the startswith attribute selector. However, some CMS’s set all links to be absolute URLS, in
which case you could assign a class to each of these anchors. It’s easy:
My About pageAn external site
To apply different styles to the external link, all you need is a rule like this:
a.external:link, a[href^="http"]:link { color: slateblue;}
a.external:visited, a[href^="http"]:visited {color: maroon;}
This rule will make the second anchor in the preceding markup slateblue by default,
and maroon once visited, while the first anchor will remain the default color for
hyperlinks (usually blue when not visited and purple once visited). For improved usa‐
bility and accessibility, visited links should be easily differentiable from non-visited
links.
Styled visited links enables visitors to know where they have been
and what they have yet to visit. This is especially important on large
websites where it may be difficult to remember, especially for those
with cognitive disabilities, which pages have been visited. Not only
is highlighting visited links one of the W3C Web Content Accessi‐
bility Guidelines, but it makes searching for content faster, more
efficient, and less stressful for everyone.
The same general syntax is used for ID selectors as well:
a#footer-copyright:link{background: yellow;}
a#footer-copyright:visited {background: gray;}
You can chain the two link-state pseudo-classes together, but there’s no reason why
you ever would: a link cannot be both visited and unvisited at the same time!
78
|
Chapter 2: Selectors
Visited Links and Privacy
For well over a decade, it was possible to style visited links with any CSS properties
available, just as you could unvisited links. However, in the mid-2000s several people
demonstrated that one could use visual styling and simple DOM scripting to deter‐
mine if a user had visited a given page. For example, given the rule :visited {fontweight: bold;}, a script could find all of the boldfaced links and tell the user which
of those sites they’d visited—or, worse still, report those sites back to a server. A simi‐
lar, non-scripted tactic uses background images to achieve the same result.
While this might not seem terribly serious to you, it can be utterly devastating for a
web user in a country where one can be jailed for visiting certain sites—opposition
parties, unsanctioned religious organizations, “immoral” or “corrupting” sites, and so
on. It can also be used by phishing sites to determine which online banks a user has
visited. Thus, two steps were taken.
The first step is that only color-related properties can be applied to visited links:
color, background-color, column-rule-color, outline-color, border-color, and
the individual-side border color properties (e.g., border-top-color). Attempts to
apply any other property to a visited link will be ignored. Furthermore, any styles
defined for :link will be applied to visited links as well as unvisited links, which
effectively makes :link “style any hyperlink,” instead of “style any unvisited hyper‐
link.”
The second step is that if a visited link has its styles queried via the DOM, the result‐
ing value will be as if the link were not visited. Thus, if you’ve defined visited links to
be purple rather than unvisited links’ blue, even though the link will appear purple
onscreen, a DOM query of its color will return the blue value, not the purple one.
As of late 2017, this behavior is present throughout all browsing modes, not just “pri‐
vate browsing” modes. Even though we’re limited in how we can use CSS to differen‐
tiate visited links from non-visited links, it is important for usability and accessibility
to use the limited styles supported by visited links to differentiate them from unvis‐
ited links.
User action pseudo-classes
CSS defines a few pseudo-classes that can change a document’s appearance based on
actions taken by the user. These dynamic pseudo-classes have traditionally been used
to style hyperlinks, but the possibilities are much wider. Table 2-3 describes these
pseudo-classes.
Pseudo-Class Selectors
|
79
Table 2-3. User action pseudo-classes
Name
:focus
Description
Refers to any element that currently has the input focus—i.e., can accept keyboard input or be activated in
some way.
:hover
Refers to any element over which the mouse pointer is placed—e.g., a hyperlink over which the mouse pointer
is hovering.
:active Refers to any element that has been activated by user input—e.g., a hyperlink on which a user clicks during the
time the mouse button is held down.
Elements that can become :active include links, buttons, menu items, and any ele‐
ment with a tabindex value. These elements and all other interactive elements, includ‐
ing form controls and elements that are content-editable, can also receive focus.
As with :link and :visited, these pseudo-classes are most familiar in the context of
hyperlinks. Many web pages have styles that look like this:
a:link {color: navy;}
a:visited {color: gray;}
a:focus {color: orange;}
a:hover {color: red;}
a:active {color: yellow;}
The order of the pseudo-classes is more important than it might
seem at first. The usual recommendation is “link-visited-hoveractive,” although this has been modified to “link-visited-focushover-active.” The next chapter explains why this particular
ordering is important and discusses several reasons you might
choose to change or even ignore the recommended ordering.
Notice that the dynamic pseudo-classes can be applied to any element, which is good
since it’s often useful to apply dynamic styles to elements that aren’t links. For exam‐
ple, using this markup:
input:focus {background: silver; font-weight: bold;}
you could highlight a form element that is ready to accept keyboard input, as shown
in Figure 2-36.
Figure 2-36. Highlighting a form element that has focus
80
|
Chapter 2: Selectors
You can also perform some rather odd feats by applying dynamic pseudo-classes to
arbitrary elements. You might decide to give users a “highlight” effect by way of the
following:
body *:hover {background: yellow;}
This rule will cause any element that’s descended from the body element to display a
yellow background when it’s in a hover state. Headings, paragraphs, lists, tables,
images, and anything else found inside the body will be changed to have a yellow
background. You could also change the font, put a border around the element being
hovered, or alter anything else the browser will allow.
While you can style elements with :focus any way you like, do not
remove all styling from focused elements. Differentiating which
element currently has focus is vital for accessibility, especially for
those navigating your site or application with a keyboard.
Real-world issues with dynamic styling
Dynamic pseudo-classes present some interesting issues and peculiarities. For exam‐
ple, it’s possible to set visited and unvisited links to one font size and make hovered
links a larger size, as shown in Figure 2-37:
a:link, a:visited {font-size: 13px;}
a:hover, a:active {font-size: 20px;}
Figure 2-37. Changing layout with dynamic pseudo-classes
As you can see, the user agent increases the size of the anchor while the mouse
pointer hovers over it; or, thanks to the :active setting, when a user touches it on a
touch screen. A user agent that supports this behavior must redraw the document
while an anchor is in hover state, which could force a reflow of all the content that
follows the link.
UI-State Pseudo-Classes
Closely related to the dynamic pseudo-classes are the user-interface (UI) state pseudoclasses, which are summarized in Table 2-4. These pseudo-classes allow for styling
based on the current state of user-interface elements like checkboxes.
Pseudo-Class Selectors
|
81
Table 2-4. UI-state pseudo-classes
Name
:enabled
Description
Refers to user-interface elements (such as form elements) that are enabled; that is, available for input.
:disabled
Refers to user-interface elements (such as form elements) that are disabled; that is, not available for
input.
:checked
Refers to radio buttons or checkboxes that have been selected, either by the user or by defaults within
the document itself.
:indetermi
nate
Refers to radio buttons or checkboxes that are neither checked nor unchecked; this state can only be set
via DOM scripting, and not due to user input.
:default
Refers to the radio button, checkbox, or option that was selected by default.
:valid
Refers to a user input that meets all of its data validity semantics
:invalid
Refers to a user input that does not meet all of its data validity semantics
:in-range
Refers to a user input whose value is between the minimum and maximum values
:out-ofrange
Refers to a user input whose value is below the minimum or above the maximum values allowed by the
control
:required
Refers to a user input that must have a value set
:optional
Refers to a user input that does not need to have a value set
:read-write
Refers to a user input that is editable by the user
:read-only
Refers to a user input that is not editable by the user
Although the state of a UI element can certainly be changed by user action—for
example, a user checking or unchecking a checkbox—UI-state pseudo-classes are not
classified as purely dynamic because they can also be affected by the document struc‐
ture or DOM scripting.
You might think that :focus belongs in this section, not the previ‐
ous section. However, the Selectors Level 3 specification
groups :focus in with :hover and :active. This is most likely
because they were grouped together in CSS2, which had no UIstate pseudo-classes. More importantly, though, focus can be given
to non-UI elements, such as headings or paragraphs—one example
is when they are read by a speaking browser. That alone keeps it
from being considered a UI-state pseudo-class.
Enabled and disabled UI elements
Thanks to both DOM scripting and HTML5, it is possible to mark a user-interface
element (or group of user interface elements) as being disabled. A disabled element is
displayed, but cannot be selected, activated, or otherwise interacted with by the user.
Authors can set an element to be disabled either through DOM scripting, or (in
HTML5) by adding a disabled attribute to the element’s markup.
82
|
Chapter 2: Selectors
Any element that hasn’t been disabled is by definition enabled. You can style these
two states using the :enabled and :disabled pseudo-classes. It’s much more com‐
mon to style disabled elements and leave enabled elements alone, but both have their
uses, as illustrated in Figure 2-38:
:enabled {font-weight: bold;}
:disabled {opacity: 0.5;}
Figure 2-38. Styling enabled and disabled UI elements
Check states
In addition to being enabled or disabled, certain UI elements can be checked or
unchecked—in HTML, the input types “checkbox” and “radio” fit this definition.
Selectors level 3 offers a :checked pseudo-class to handle elements in that state,
though curiously it omits an :unchecked. There is also the :indeterminate pseudoclass, which matches any checkable UI element that is neither checked nor
unchecked. These states are illustrated in Figure 2-39:
:checked {background: silver;}
:indeterminate {border: red;}
In addition, you can use the negation pseudo-class, which is covered later, to select
checkboxes which are not checked with input[type="checkbox]:not(:checked).
Only radio buttons and checkboxes can be checked. All other elements, and these two
when not checked, are :not(:checked).
Figure 2-39. Styling checked and indeterminate UI elements
Although checkable elements are unchecked by default, it’s possible for a HTML
author to toggle them on by adding the checked attribute to an element’s markup. An
author can also use DOM scripting to flip an element’s checked state to checked or
unchecked, whichever they prefer.
There is a third state, “indeterminate.” As of late 2017, this state can only be set
through DOM scripting or by the user agent itself; there is no markup-level method
to set elements to an indeterminate state. The purpose of allowing an indeterminate
state is to visually indicate that the element needs to be checked (or unchecked) by
the user. However, note that this is purely a visual effect: it does not affect the under‐
Pseudo-Class Selectors
|
83
lying state of the UI element, which is either checked or unchecked, depending on
document markup and the effects of any DOM scripting.
Although the previous examples show styled radio buttons, remember that direct
styling of radio buttons and checkboxes with CSS is actually very limited. However,
that shouldn’t limit your use of the selected-option pseudo-classes. As an example,
you can style the labels associated with your checkboxes and radio buttons using a
combination of :checked and the adjacent sibling combinator:
input[type="checkbox"]:checked + label {
color: red;
font-style: italic;
}
Default option pseudo-class
The :default pseudo-class matches the UI elements that are the default among a set
of similar elements. This typically applies to context menu items, buttons, and select
lists/menus. If there are several same-named radio buttons, the one that was origi‐
nally checked matches :default, even if the UI has been updated by the user so that
it no longer matches :checked. If a checkbox was checked on page load, :default
matches it. Any initially-selected option(s) in a select element will match.
The :default pseudo-class can also match buttons and menu items:
[type="checkbox"]:default + label { font-style: italic; }
Optionality pseudo-classes
The pseudo-class :required matches any form control that is required, as denoted by
the presence of the required attribute (in HTML5). The :optional pseudo-class
matches form controls that do not have the required attribute, or whose required
attribute has a value of false.
A form element is :required or :optional if a value for it is, respectively, required or
optional before the form to which it belongs can be validly submitted. For example:
input:required { border: 1px solid #f00;}
input:optional { border: 1px solid #ccc;}
The first email input will match the :required pseudo-class because of the presence
of the required attribute. The second input is optional, and therefore will match
84
| Chapter 2: Selectors
the :optional pseudo-class. The same is true for the third input, which has a
required attribute, but the value is false.
We could also use attribute selectors instead. The following selectors are equivalent to
the preceding ones:
input[required] { border: 1px solid #f00;}
input:not([required]) { border: 1px solid #ccc;}
Elements that are not form-input elements can be neither required nor optional.
Validity pseudo-classes
The :valid pseudo-class refers to a user input that meets all of its data validity
requirements. The :invalid pseudo-class, on the other hand, refers to a user input
that does not meet all of its data validity requirements.
The validity pseudo-classes :valid and :invalid only apply to elements having the
capacity for data validity requirements: a div will never match either selector, but an
input could match either, depending on the current state of the interface.
Here’s an example where an image is dropped into the background of any email input
which has focus, with one image being used when the input is invalid and another
used when the input is valid, as illustrated in Figure 2-40:
input[type="email"]:focus {
background-position: 100% 50%;
background-repeat: no-repeat;
}
input[type="email"]:focus:invalid {
background-image: url(warning.jpg);
}
input[type="email"]:focus:valid {
background-image: url(checkmark.jpg);
}
Figure 2-40. Styling valid and invalid UI elements
Pseudo-Class Selectors
|
85
These pseudo-class states are dependent on what the user agent
reports to its own style system, and so may not act as you might
expect. For example, in late 2017, an empty email input
matched :valid in multiple user agents, despite the fact a null
input is not a valid email address. Until these validation routines
improve, it is best to treat the validity pseudo-classes with caution.
Range pseudo-classes
The range pseudo-classes include :in-range, which refers to a user input whose
value is between the minimum and maximum values set by HTML5’s min and max
attributes, and :out-of-range, which refers to a user input whose value is below the
minimum or above the maximum values allowed by the control.
For example, consider a number input that accepts numbers in the range 0 to 1,000:
input[type="number"]:focus {
background-position: 100% 50%;
background-repeat: no-repeat;
}
input[type="number"]:focus:out-of-range {
background-image: url(warning.jpg);
}
input[type="number"]:focus:in-range {
background-image: url(checkmark.jpg);
}
The :in-range and :out-of-range pseudo-classes apply only to elements with range
limitations. Elements that don’t have range limitations, like links for inputs of type
tel, will not be matched by either pseudo-class.
There is also a step attribute in HTML5. If a value is invalid because it does not
match the step value, but is still between or equal to the min and max values, it will
match :invalid while also still matching :in-range. That is to say, a value can be inrange while also being invalid.
Thus, in the following scenario, the input’s value will be both red and boldfaced,
because 23 is in range but is not evenly divisible by 10:
input[type="number"]:invalid {color: red;}
input[type="number"]:in-range {font-weight: bold;}
Mutability pseudo-classes
The mutability pseudo-classes include :read-write, which refers to a user input that
is editable by the user; and :read-only, which matches user inputs that are not edita‐
86
|
Chapter 2: Selectors
ble. Only elements that have the capacity to be altered by user input can
match :read-write.
For example, in HTML, a non-disabled, non-read-only input element is :readwrite, as is any element with the contenteditable attribute. Everything else
matches :read-only:
By default, neither of the following rules would ever match: textarea elements are
read-write, and pre elements are read-only.
textarea:read-only { opacity: 0.75;}
pre:read-write:hover {border: 1px dashed green;}
However, each can be made to match as follows:
Type your own code!
Because the textarea is given a disabled attribute, it becomes read-only, and so will
have the first rule apply. Similarly, the pre here has been given the attribute contente
ditable, so now it is a read-write element. This will be matched by the second rule.
The :target Pseudo-Class
When a URL includes a fragment identifier, the piece of the document at which it
points is called (in CSS) the target. Thus, you can uniquely style any element that is
the target of a URL fragment identifier with the :target pseudo-class.
Even if you’re unfamiliar with the term “fragment identifier,” you’ve probably seen
them in action. Consider this URL:
http://www.w3.org/TR/css3-selectors/#target-pseudo
The target-pseudo portion of the URL is the fragment identifier, which is marked by
the # symbol. If the referenced page (http://www.w3.org/TR/css3-selectors/) has an ele‐
ment with an ID of target-pseudo, then that element becomes the target of the frag‐
ment identifier.
Thanks to :target, you can highlight any targeted element within a document, or
you can devise different styles for various types of elements that might be targeted—
say, one style for targeted headings, another for targeted tables, and so on. Figure 2-41
shows an example of :target in action:
*:target {border-left: 5px solid gray; background: yellow url(target.png)
top right no-repeat;}
Pseudo-Class Selectors
|
87
Figure 2-41. Styling a fragment identifier target
:target styles will not be applied in two circumstances:
1. If the page is accessed via a URL that does not have a fragment identifier
2. If the page is accessed via a URL that has a fragment identifier, but the identifier
does not match any elements within the document
More interestingly, though, what happens if multiple elements within a document can
be matched by the fragment identifier—for example, if the author erroneously
included three separate instances of
in the same docu‐
ment?
The short answer is that CSS doesn’t have or need rules to cover this case, because all
CSS is concerned with is styling targets. Whether the browser picks just one of the
three elements to be the target or designates all three as co-equal targets, :target
styles should be applied to anything that is a valid target.
The :lang Pseudo-Class
For situations where you want to select an element based on its language, you can use
the :lang() pseudo-class. In terms of its matching patterns, the :lang() pseudo-class
is similar to the |= attribute selector. For example, to italicize elements whose content
is written in French, you could write either of the following:
*:lang(fr) {font-style: italic;}
*[lang|="fr"] {font-style: italic;}
The primary difference between the pseudo-class selector and the attribute selector is
that language information can be derived from a number of sources, some of which
are outside the element itself. For the attribute selector, the element must have the
88
|
Chapter 2: Selectors
attribute present to match. The :lang pseudo-class, on the other hand, matches
descendants of an element with the language declaration. As Selectors Level 3 states:
In HTML, the language is determined by a combination of the lang attribute, and pos‐
sibly information from the meta elements and the protocol (such as HTTP headers).
XML uses an attribute called xml:lang, and there may be other document languagespecific methods for determining the language.
The pseudo-class will operate on all of that information, whereas the attribute selec‐
tor can only work if there is a lang attribute present in the element’s markup. There‐
fore, the pseudo-class is more robust than the attribute selector and is probably a
better choice in most cases where language-specific styling is needed.
The Negation Pseudo-Class
Every selector we’ve covered thus far has had one thing in common: they’re all posi‐
tive selectors. In other words, they are used to identify the things that should be
selected, thus excluding by implication all the things that don’t match and are thus
not selected.
For those times when you want to invert this formulation and select elements based
on what they are not, Selectors Level 3 introduced the negation pseudo-class, :not().
It’s not quite like any other selector, fittingly enough, and it does have some restric‐
tions on its use, but let’s start with an example.
Let’s suppose you want to apply a style to every list item that doesn’t have a class of
moreinfo, as illustrated in Figure 2-42. That used to be very difficult, and in certain
cases impossible, to make happen. If we wanted all the list items to be italic except
those with the class .moreinfo, we used to declare all the links as italic, generally hav‐
ing to target the ul with a class, then normalize back based on the class, making sure
the override came last in the source order, and had equal or higher specificity. Now
we can declare:
li:not(.moreinfo) {font-style: italic;}
Figure 2-42. Styling list items that don’t have a certain class
Pseudo-Class Selectors
|
89
The way :not() works is that you attach it to an element, and then in the parentheses
you fill in a simple selector. A simple selector, according to the W3C, is:
either a type selector, universal selector, attribute selector, class selector, ID selector, or
pseudo-class.
Basically, a simple selector is a selector with no ancestral-descendant relationship.
Note the “either” there: you can only use one of those inside :not(). You can’t group
them and you can’t combine them using combinators, which means you can’t use a
descendant selector, because the space separating elements in a descendant selector is
a combinator. Those restrictions may (indeed most likely will) be lifted in the future,
but we can still do quite a lot even within the given constraints.
For example, let’s flip around the previous example and select all elements with a
class of moreinfo that are not list items. This is illustrated in Figure 2-43:
.moreinfo:not(li) {font-style: italic;}
Figure 2-43. Styling elements with a certain class that aren’t list items
Translated into English, the selector would say, “Select all elements with a class
whose value contains the word moreinfo as long as they are not li elements.” Simi‐
larly, the translation of li:not(.moreinfo) would be “Select all li elements as long
as they do not have a class whose value contains the word moreinfo.”
Technically, you can put a universal selector into the parentheses, but there’s very lit‐
tle point. After all, p:not(*) would mean “Select any p element as long as it isn’t any
element,” and there’s no such thing as an element that is not an element. Very similar
to that would be p:not(p), which would also select nothing. It’s also possible to write
things like p:not(div), which will select any p element that is not a div element—in
other words, all of them. Again, there is very little reason to do so.
You can also use the negation pseudo-class at any point in a more complex selector.
Thus, to select all tables that are not children of a section element, you would write
*:not(section) > table. Similarly, to select table header cells that are not part of
the table header, you’d write something like table *:not(thead) > tr > th, with a
result like that shown in Figure 2-44.
90
|
Chapter 2: Selectors
Figure 2-44. Styling header cells outside the table’s head area
What you cannot do is nest negation pseudo-classes; thus, p:not(:not(p)) is invalid
and will be ignored. It’s also, logically, the equivalent of just writing p, so there’s no
point anyway. Furthermore, you cannot reference pseudo-elements (which we’ll cover
shortly) inside the parentheses, since they are not simple selectors.
On the other hand, it’s possible to chain negations together to create a sort of “and
also not this” effect. For example, you might want to select all elements with a class
of link that are neither list items nor paragraphs:
*.link:not(li):not(p) {font-style: italic;}
That translates to “Select all elements with a class whose value contains the word
link as long as they are neither li nor p elements.”
One thing to watch out for is that you can have situations where rules combine in
unexpected ways, mostly because we’re not used to thinking of selection in the nega‐
tive. Consider this test case:
div:not(.one) p {font-weight: normal;}
div.one p {font-weight: bold;}
I'm a paragraph!
The paragraph will be boldfaced, not normal-weight. This is because both rules
match: the p element is descended from a div whose class does not contain the word
one (
), but it is also descended from a div whose class contains
the word one. Both rules match, and so both apply. Since there is a conflict, the cas‐
cade is used to resolve the conflict, and the second rule wins. The structural arrange‐
ment of the markup, with the div.two being “closer” to the paragraph than div.one,
is irrelevant.
Pseudo-Class Selectors
|
91
Pseudo-Element Selectors
Much as pseudo-classes assign phantom classes to anchors, pseudo-elements insert
fictional elements into a document in order to achieve certain effects. Four basic
pseudo-elements were defined in CSS 2, and they let you style the first letter of an
element, style the first line of an element, and both create and style “before” and
“after” content. There are other pseudo-classes that have been defined since CSS 2
(e.g., ::marker), and we’ll explore those in the chapters of the book for which they’re
relevant. The four from CSS2 will be covered here because they’re old-school, and
because they make a convenient way to talk about pseudo-element behavior.
Unlike the single colon of pseudo-classes, pseudo-elements employ a double-colon
syntax, like ::first-line. This is meant to distinguish pseudo-elements from
pseudo-classes. This was not always the case—in CSS2, both selector types used a sin‐
gle colon—so for backward compatibility, browsers will accept single-colon pseudoelement selectors. Don’t take this as an excuse to be sloppy, though! Use the proper
number of colons at all times in order to future-proof your CSS; after all, there is no
way to predict when browsers will stop accepting single-colon pseudo-element selec‐
tors.
Note that all pseudo-elements must be placed at the very end of the selector in which
they appear. It would not be legal to write p::first-line em since the pseudoelement comes before the subject of the selector (the subject is the last element listed).
This also means that only one pseudo-element is permitted in a given selector,
though that restriction may be eased in future versions of CSS.
Styling the First Letter
The ::first-letter pseudo-element styles the first letter, or a leading punctuation
character and the first letter (if the text starts with punctuation), of any non-inline
element. This rule causes the first letter of every paragraph to be colored red:
p::first-letter {color: red;}
The ::first-letter pseudo-element is most commonly used to create an “initial
cap” or “drop cap” typographic effect. You could make the first letter of each p twice
as big as the rest of the heading, though you may want to only apply this styling to the
first letter of the first paragraph:
p:first-of-type::first-letter {font-size: 200%;}
The result of this rule is illustrated in Figure 2-45.
Figure 2-45. The ::first-letter pseudo-element in action
92
|
Chapter 2: Selectors
This rule effectively causes the user agent to style a fictional, or “faux” element, that
encloses the first letter of each p. It would look something like this:
This is a p element, with a styled first
letter
The ::first-letter styles are applied only to the contents of the fictional element
shown in the example. This element does not appear in the docu‐
ment source, nor even in the DOM tree. Instead, its existence is constructed on the
fly by the user agent and is used to apply the ::first-letter style(s) to the appropri‐
ate bit of text. In other words, is a pseudo-element. Remember,
you don’t have to add any new tags. The user agent styles the first letter for you as if
you had encased it in a styled element.
The first letter is defined as the first typographic letter unit of the originating element,
if it is not preceded by other content, like an image. The specifications use “letter
unit” because some languages have letters made up of more than character, like “oe”
in Norse. Punctuation that precedes or follows the first letter unit, even if there are
several such symbols, are included in the ::first-letter pseudo-element.
Styling the First Line
Similarly, ::first-line can be used to affect the first line of text in an element. For
example, you could make the first line of each paragraph in a document large and
purple:
p::first-line {
font-size: 150%;
color: purple;
}
In Figure 2-46, the style is applied to the first displayed line of text in each paragraph.
This is true no matter how wide or narrow the display region is. If the first line con‐
tains only the first five words of the paragraph, then only those five words will be big
and purple. If the first line contains the first 30 words of the element, then all 30 will
be big and purple.
Figure 2-46. The ::first-line pseudo-element in action
Because the text from “This” to “only” should be big and purple, the user agent
employs a fictional markup that looks something like this:
Pseudo-Element Selectors
|
93
This is a paragraph of text that has only
one stylesheet applied to it. That style causes the first line to
be big and purple. No other line will have those styles applied.
If the first line of text were edited to include only the first seven words of the para‐
graph, then the fictional would move back and occur just after the
word “that.” If the user were to increase or decrease the font-size rendering, or
expand or contract the browser window causing the width of the text to change,
thereby causing the number of words on the first line to increase or decrease, the
browser automatically sets only the words in the currently displayed first line to be
both big and purple.
The length of the first line depends on a number of factors, including the font-size,
letter spacing, width of the parent container, etc. Depending on the markup, and the
length of that first line, it is possible that the end of the first line comes in the middle
of a nested element. If the ::first-line breaks up a nested element, such as an em or
a hyperlink, the properties attached to the ::first-line will only apply to the por‐
tion of that nested element that is displayed on the first line.
Restrictions on ::first-letter and ::first-line
The ::first-letter and ::first-line pseudo-elements currently can be applied
only to block-display elements such as headings or paragraphs, and not to inlinedisplay elements such as hyperlinks. There are also limits on the CSS properties that
may be applied to ::first-line and ::first-letter. Table 2-5 gives an idea of
these limitations.
Table 2-5. Properties permitted on pseudo-elements
::first-letter
::first-line
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
94
All font properties
All background properties
All text decoration properties
All inline typesetting properties
All inline layout properties
All border properties
box-shadow
color
opacity
|
Chapter 2: Selectors
All font properties
All background properties
All margin properties
All padding properties
All border properties
All text decoration properties
All inline typesetting properties
color
opacity
Styling (or Creating) Content Before and After Elements
Let’s say you want to preface every h2 element with a pair of silver square brackets as
a typographical effect:
h2::before {content: "]]"; color: silver;}
CSS lets you insert generated content, and then style it directly using the pseudoelements ::before and ::after. Figure 2-47 illustrates an example.
Figure 2-47. Inserting content before an element
The pseudo-element is used to insert the generated content and to style it. To place
content after an element, use the pseudo-element ::after. You could end your docu‐
ments with an appropriate finish:
body::after {content: "The End.";}
Generated content is a separate subject, and the entire topic (including more detail
on ::before and ::after) is covered more thoroughly in Chapter 15.
Summary
By using selectors based on the document’s language, authors can create CSS rules
that apply to a large number of similar elements just as easily as they can construct
rules that apply in very narrow circumstances. The ability to group together both
selectors and rules keeps stylesheets compact and flexible, which incidentally leads to
smaller file sizes and faster download times.
Selectors are the one thing that user agents usually must get right because the inability
to correctly interpret selectors pretty much prevents a user agent from using CSS at
all. On the flip side, it’s crucial for authors to correctly write selectors because errors
can prevent the user agent from applying the styles as intended. An integral part of
correctly understanding selectors and how they can be combined is a strong grasp of
how selectors relate to document structure and how mechanisms—such as inheri‐
tance and the cascade itself—come into play when determining how an element will
be styled.
Summary
|
95
CHAPTER 3
Specificity and the Cascade
Chapter 2 showed how document structure and CSS selectors allow you to apply a
wide variety of styles to elements. Knowing that every valid document generates a
structural tree, you can create selectors that target elements based on their ancestors,
attributes, sibling elements, and more. The structural tree is what allows selectors to
function and is also central to a similarly crucial aspect of CSS: inheritance.
Inheritance is the mechanism by which some property values are passed on from an
element to its descendants. When determining which values should apply to an ele‐
ment, a user agent must consider not only inheritance but also the specificity of the
declarations, as well as the origin of the declarations themselves. This process of con‐
sideration is what’s known as the cascade. We will explore the interrelation between
these three mechanisms—specificity, inheritance, and the cascade—in this chapter,
but the difference between the latter two can be summed up this way: choosing the
result of h1 {color: red; color: blue;} is the cascade; making a span inside the
h1 blue is inheritance.
Above all, regardless of how abstract things may seem, keep going! Your perseverance
will be rewarded.
Specificity
You know from Chapter 2 that you can select elements using a wide variety of means.
In fact, it’s possible that the same element could be selected by two or more rules,
each with its own selector. Let’s consider the following three pairs of rules. Assume
that each pair will match the same element:
h1 {color: red;}
body h1 {color: green;}
h2.grape {color: purple;}
97
h2 {color: silver;}
html > body table tr[id="totals"] td ul > li {color: maroon;}
li#answer {color: navy;}
Only one of the two rules in each pair can win out, since the matched elements can be
only one color or the other. How do we know which one will win?
The answer is found in the specificity of each selector. For every rule, the user agent
evaluates the specificity of the selector and attaches it to each declaration in the rule.
When an element has two or more conflicting property declarations, the one with the
highest specificity will win out.
This isn’t the whole story in terms of conflict resolution. All style
conflict resolution (including specificity) is handled by the cascade,
which has its own section later in this chapter (“The Cascade” on
page 106).
A selector’s specificity is determined by the components of the selector itself. A spe‐
cificity value can be expressed in four parts, like this: 0,0,0,0. The actual specificity
of a selector is determined as follows:
• For every ID attribute value given in the selector, add 0,1,0,0.
• For every class attribute value, attribute selection, or pseudo-class given in the
selector, add 0,0,1,0.
• For every element and pseudo-element given in the selector, add 0,0,0,1. CSS2
contradicted itself as to whether pseudo-elements had any specificity at all, but
CSS2.1 made it clear that they do, and this is where they belong.
• Combinators and the universal selector do not contribute anything to the specif‐
icity.
For example, the following rules’ selectors result in the indicated specificities:
h1 {color: red;}
p em {color: purple;}
.grape {color: purple;}
*.bright {color: yellow;}
p.bright em.dark {color: maroon;}
#id216 {color: blue;}
div#sidebar *[href] {color: silver;}
/*
/*
/*
/*
/*
/*
/*
specificity
specificity
specificity
specificity
specificity
specificity
specificity
=
=
=
=
=
=
=
0,0,0,1
0,0,0,2
0,0,1,0
0,0,1,0
0,0,2,2
0,1,0,0
0,1,1,1
*/
*/
*/
*/
*/
*/
*/
Given a case where an em element is matched by both the second and fifth rules in
this example, that element will be maroon because the fifth rule’s specificity out‐
weighs the second’s.
98
|
Chapter 3: Specificity and the Cascade
As an exercise, let’s return to the pairs of rules from earlier in the section and fill in
the specificities:
h1 {color: red;}
body h1 {color: green;}
h2.grape {color: purple;}
h2 {color: silver;}
/* 0,0,0,1 */
/* 0,0,0,2 (winner)*/
/* 0,0,1,1 (winner) */
/* 0,0,0,1 */
html > body table tr[id="totals"] td ul > li {color: maroon;}
li#answer {color: navy;}
(winner) */
/* 0,0,1,7 */
/* 0,1,0,1
I’ve indicated the winning rule in each pair; in each case, it’s because the specificity is
higher. Notice how they’re sorted. In the second pair, the selector h2.grape wins
because it has an extra 1: 0,0,1,1 beats out 0,0,0,1. In the third pair, the second rule
wins because 0,1,0,1 wins out over 0,0,1,7. In fact, the specificity value 0,0,1,0
will win out over the value 0,0,0,13.
This happens because the values are sorted from left to right. A specificity of 1,0,0,0
will win out over any specificity that begins with a 0, no matter what the rest of the
numbers might be. So 0,1,0,1 wins over 0,0,1,7 because the 1 in the first value’s
second position beats out the 0 in the second value’s second position.
Declarations and Specificity
Once the specificity of a selector has been determined, the specificity value will be
conferred on all of its associated declarations. Consider this rule:
h1 {color: silver; background: black;}
For specificity purposes, the user agent must treat the rule as if it were “ungrouped”
into separate rules. Thus, the previous example would become:
h1 {color: silver;}
h1 {background: black;}
Both have a specificity of 0,0,0,1, and that’s the value conferred on each declaration.
The same splitting-up process happens with a grouped selector as well. Given the
rule:
h1, h2.section {color: silver; background: black;}
the user agent treats it if it were the following:
h1 {color: silver;}
h1 {background: black;}
h2.section {color: silver;}
h2.section {background: black;}
/*
/*
/*
/*
0,0,0,1
0,0,0,1
0,0,1,1
0,0,1,1
*/
*/
*/
*/
Specificity |
99
This becomes important in situations where multiple rules match the same element
and some of the declarations clash. For example, consider these rules:
h1 + p {color: black; font-style: italic;}
/* 0,0,0,2 */
p {color: gray; background: white; font-style: normal;} /* 0,0,0,1 */
*.aside {color: black; background: silver;}
/* 0,0,1,0 */
When applied to the following markup, the content will be rendered as shown in
Figure 3-1:
Greetings!
It's a fine way to start a day, don't you think?
There are many ways to greet a person, but the words are not as important as
the act of greeting itself.
Salutations!
There is nothing finer than a hearty welcome from one's fellow man.
Although a thick and juicy hamburger with bacon and mushrooms runs a close second.
Figure 3-1. How different rules affect a document
In every case, the user agent determines which rules match a given element, calculates
all of the associated declarations and their specificities, determines which rules win
out, and then applies the winners to the element to get the styled result. These machi‐
nations must be performed on every element, selector, and declaration. Fortunately,
the user agent does it all automatically. This behavior is an important component of
the cascade, which we will discuss later in this chapter.
100
|
Chapter 3: Specificity and the Cascade
Universal Selector Specificity
The universal selector does not contribute to specificity. In other words, it has a spe‐
cificity of 0,0,0,0, which is different than having no specificity (as we’ll discuss in
“Inheritance” on page 103). Therefore, given the following two rules, a paragraph
descended from a div will be black, but all other elements will be gray:
div p {color: black;} /* 0,0,0,2 */
* {color: gray;}
/* 0,0,0,0 */
As you might expect, this means the specificity of a selector that contains a universal
selector along with other selectors is not changed by the presence of the universal
selector. The following two selectors have exactly the same specificity:
div p
/* 0,0,0,2 */
body * strong /* 0,0,0,2 */
Combinators, by comparison, have no specificity at all—not even zero specificity.
Thus, they have no impact on a selector’s overall specificity.
ID and Attribute Selector Specificity
It’s important to note the difference in specificity between an ID selector and an
attribute selector that targets an id attribute. Returning to the third pair of rules in
the example code, we find:
html > body table tr[id="totals"] td ul > li {color: maroon;} /* 0,0,1,7 */
li#answer {color: navy;}
/* 0,1,0,1 (wins) */
The ID selector (#answer) in the second rule contributes 0,1,0,0 to the overall spe‐
cificity of the selector. In the first rule, however, the attribute selector
([id="totals"]) contributes 0,0,1,0 to the overall specificity. Thus, given the fol‐
lowing rules, the element with an id of meadow will be green:
#meadow {color: green;}
/* 0,1,0,0 */
*[id="meadow"] {color: red;} /* 0,0,1,0 */
Inline Style Specificity
So far, we’ve only seen specificities that begin with a zero, so you may be wondering
why it’s there at all. As it happens, that first zero is reserved for inline style declara‐
tions, which trump any other declaration’s specificity. Consider the following rule and
markup fragment:
h1 {color: red;}
The Meadow Party
Specificity |
101
Given that the rule is applied to the h1 element, you would still probably expect the
text of the h1 to be green. This happens because every inline declaration has a specif‐
icity of 1,0,0,0.
This means that even elements with id attributes that match a rule will obey the
inline style declaration. Let’s modify the previous example to include an id:
h1#meadow {color: red;}
The Meadow Party
Thanks to the inline declaration’s specificity, the text of the h1 element will still be
green.
Importance
Sometimes, a declaration is so important that it outweighs all other considerations.
CSS calls these important declarations (for hopefully obvious reasons) and lets you
mark them by inserting !important just before the terminating semicolon in a decla‐
ration:
p.dark {color: #333 !important; background: white;}
Here, the color value of #333 is marked !important, whereas the background value of
white is not. If you wish to mark both declarations as important, each declaration
needs its own !important marker:
p.dark {color: #333 !important; background: white !important;}
You must place !important correctly, or the declaration may be invalidated. !impor
tant always goes at the end of the declaration, just before the semicolon. This place‐
ment is especially important—no pun intended—when it comes to properties that
allow values containing multiple keywords, such as font:
p.light {color: yellow; font: smaller Times, serif !important;}
If !important were placed anywhere else in the font declaration, the entire declara‐
tion would likely be invalidated and none of its styles applied.
I realize that to those of you who come from a programming back‐
ground, the syntax of this token instinctively translates to “not
important.” For whatever reason, the bang (!) was chosen as the
delimiter for important tokens, and it does not mean “not” in CSS,
no matter how many other languages give it that very meaning.
This association is unfortunate, but we’re stuck with it.
Declarations that are marked !important do not have a special specificity value, but
are instead considered separately from non-important declarations. In effect, all !
102
|
Chapter 3: Specificity and the Cascade
important declarations are grouped together, and specificity conflicts are resolved
relatively within that group. Similarly, all non-important declarations are considered
together, with any conflicts within the non-important group are resolved using spe‐
cificity. Thus, in any case where an important and a non-important declaration con‐
flict, the important declaration always wins.
Figure 3-2 illustrates the result of the following rules and markup fragment:
h1 {font-style: italic; color: gray !important;}
.title {color: black; background: silver;}
* {background: black !important;}
NightWing
Figure 3-2. Important rules always win
Important declarations and their handling are discussed in more
detail in “The Cascade” on page 106.
Inheritance
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 some styles are applied not only to a specified element, but also to its descend‐
ants. If a color is applied to an h1 element, for example, then that color is applied to
all text inside the h1, even the text enclosed within child elements of that h1:
h1 {color: gray;}
Meerkat Central
Both the ordinary h1 text and the em text are colored gray because the em element
inherits the value of color from the h1. If property values could not be inherited by
descendant elements, the em text would be black, not gray, and we’d have to color the
elements separately.
Consider an unordered list. Let’s say we apply a style of color: gray; for ul ele‐
ments:
ul {color: gray;}
Inheritance
|
103
We expect that style applied to a ul will also be applied to its list items, and also 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 declaration color: gray; is applied to the ul element, that element takes
on that declaration. The value is then propagated down the tree to the descendant ele‐
ments and continues on until there are no more descendants to inherit the value. Val‐
ues 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 the body element can be passed to the
html element, which is the document’s root element and therefore
defines its canvas. This only happens if the body element has a
defined background and the html element does not.
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 couple of things in
mind.
First, note that many properties are not inherited—generally in order to avoid unde‐
sirable outcomes. For example, the property border (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
104
|
Chapter 3: Specificity and the Cascade
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, back‐
grounds, and borders—are not inherited for the same reason. After all, you likely
wouldn’t want all of the links in a paragraph to inherit a 30-pixel left margin from
their parent element!
Second, 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
Since the universal selector applies to all elements and has zero specificity, its color
declaration’s value of gray wins out over the inherited value of black, which has no
specificity at all. Therefore, the em 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 uni‐
versal selector indiscriminately.
Inheritance
|
105
The complete lack of specificity for inherited values is not a trivial point. For exam‐
ple, 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 so long as the element with an id of toolbar 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 internal style sheet probably contains
an entry like this:
a:link {color: blue;}
To overcome this problem, you must declare something like this:
#toolbar {color: white; background: black;}
#toolbar a:link {color: white;}
By targeting a rule directly at the a elements within the toolbar, you’ll get the result
shown in Figure 3-7.
Figure 3-7. Directly assigning styles to the relevant elements
Another way to get the same result is to use the value inherit, covered in the previ‐
ous chapter. We can alter the previous example like so:
#toolbar {color: white; background: black;}
#toolbar a:link {color: inherit;}
This also leads to the result shown in Figure 3-7, because the value of color is explic‐
itly inherited thanks to an assigned rule whose selector has specificity.
The Cascade
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, consider the following rules:
h1 {color: red;}
h1 {color: blue;}
Which one wins? Both have a specificity of 0,0,0,1, so they have equal weight and
should both apply. That can’t be the case because the element can’t be both red and
blue. So which will it be?
106
|
Chapter 3: Specificity and the Cascade
At last, the name “Cascading Style Sheets” makes sense: CSS is based on a method of
causing styles to cascade together, which is made possible by combining inheritance
and specificity with a few rules. The cascade rules for CSS are:
1. Find all rules that contain a selector that matches a given element.
2. Sort all declarations applying to the given element by explicit weight. Those rules
marked !important have a higher weight than those that are not.
3. Sort all declarations applying to the given element by origin. There are three basic
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.
4. Sort all declarations applying to the given element by specificity. Those elements
with a higher specificity have more weight than those with lower specificity.
5. Sort all declarations applying to the given element by order. The later a declara‐
tion appears in the style sheet or document, the more weight it is given. Declara‐
tions 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 some examples that illus‐
trate the last four of the five cascade rules.
Some later CSS modules add more origins to the basic list of three;
for example, the animation and transition origins. These are not
covered here, but are addressed in the chapters on those topics.
Sorting by Weight and Origin
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 the style attribute of the paragraph,
the !important rule wins out, and the paragraph is gray. This gray is inherited by the
em element as well.
Note that if an !important is added to the inline style, then it will be the winner.
Thus, given the following, the paragraph (and its descendant element) will be black:
p {color: gray !important;}
The Cascade
|
107
Well, hello there!
In situations where the explicit weight is the same, 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., declar‐
ing them to be white), then this rule overrides the user agent’s defaults.
To sum up, there are five basic levels to consider in terms of declaration weight. In
order of most to least weight, these are:
1. Reader important declarations
2. Author important declarations
3. Author normal declarations
4. Reader normal declarations
5. User agent declarations
Authors typically need to worry about only the first four weight levels, since anything
declared by an author will win out over the user agent’s styles.
Sorting by Specificity
If conflicting declarations apply to an element and they all have the same explicit
weight and origin, they should be sorted by specificity, with the most specific declara‐
tion winning out, like this:
p#bright {color: silver;}
p {color: black;}
Well, hello there!
108
| Chapter 3: Specificity and the Cascade
Given the rules shown, the text of the paragraph will be silver, as illustrated in
Figure 3-8. Why? Because the specificity of p#bright (0,1,0,1) overrode the specif‐
icity of p (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
Sorting by Order
Finally, if two rules have exactly the same explicit weight, origin, and specificity, then
the one that occurs later in the style sheet wins out. Let’s return to our earlier exam‐
ple, where we find the following two rules in the document’s style sheet:
h1 {color: red;}
h1 {color: blue;}
In this case, the value of color for all h1 elements in the document will be blue, not
red. This is because the two rules are tied with each other in terms of explicit weight
and origin, and the selectors have equal specificity, so the last one declared is the win‐
ner.
So what happens if rules from completely separate style sheets conflict? For example,
suppose the following:
@import url(basic.css);
h1 {color: blue;}
What if h1 {color: red;} appears in basic.css? The entire contents of basic.css
are treated as if they were pasted into the style sheet at the point where the @import
occurs. Thus, any rule contained in the document’s style sheet occurs later than those
from the @import. If they tie in terms of explicit weight and specificity, the docu‐
ment’s style sheet contains the winner. 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 wins out over the imported rule because it was the
last one specified.
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-focushover-active, or LVFHA, like this:
a:link {color: blue;}
a:visited {color: purple;}
a:focus {color: green;}
The Cascade
|
109
a:hover {color: red;}
a: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 explicit weight,
origin, and specificity, the last one that matches an element will win out. An unvisited
link that is being “clicked” or otherwise activated, such as via the keyboard, is
matched by four of the rules—:link, Lfocus, :hover, and :active—so the last one
of those four will win out. Given the LVFHA ordering, :active will win, which is
likely what the author intended.
Assume for a moment that you decide to ignore the common ordering and alphabet‐
ize your link styles instead. This would yield:
a:active {color: orange;}
a:focus {color: green;}
a:hover {color: red;}
a:link {color: blue;}
a:visited {color: purple;}
Given this ordering, no link would ever show :hover, :focus, or :active styles
because the :link and :visited rules come after the other three. Every link must be
either visited or unvisited, so those styles will always override the others.
Let’s consider a variation on the LVFHA order that an author might want to use. In
this ordering, only unvisited links will get a hover style; visited links do not. Both vis‐
ited and unvisited links will get an active style:
a:link {color: blue;}
a:hover {color: red;}
a:visited {color: purple;}
a:focus {color: green;}
a:active {color: orange;}
Such conflicts arise only when all the states attempt to set the same property. If each
state’s styles address a different property, then the order does not matter. In the fol‐
lowing case, the link styles could be given in any order and would still function as
intended:
a:link {font-weight: bold;}
a:visited {font-style: italic;}
a:focus {color: green;}
a:hover {color: red;}
a: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 LVFHA or VLFHA with no ill effect.
The ability to chain pseudo-classes together eliminates all these worries. The follow‐
ing could be listed in any order without any overrides:
110
|
Chapter 3: Specificity and the Cascade
a:link {color: blue;}
a:visited {color: purple;}
a:link:hover {color: red;}
a:visited:hover {color: gray;}
Because each rule applies to a unique set of link states, they do not conflict. There‐
fore, 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:
a:link {color: blue;}
a:visited {color: purple;}
a:link:hover {color: red;}
a:visited:hover {color: gray;}
a:link:active {color: orange;}
a: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:
a:link:hover:active {color: orange;}
a:visited:hover:active {color: silver;}
This does have the effect of raising the specificity of the selectors—both have a specif‐
icity value of 0,0,3,1—but they don’t conflict because the actual selection states are
mutually exclusive. A link can’t be an unvisited hovered active link and an unvisited
hovered active link: only one of the two rules will match, and the styles applied
accordingly.
Non-CSS Presentational Hints
It is possible that a document will contain presentational hints that are not CSS—for
example, the font element. Such presentational hints are treated as if they have a spe‐
cificity of 0 and appear at the beginning of the author’s stylesheet. Such presentation
hints will be overridden by any author or reader styles, but not by the user agent’s
styles. In CSS3, presentational hints from outside CSS are treated as if they belong to
the user agent’s stylesheet, presumably at the end (although as of this writing, the
specification doesn’t say).
Summary
Perhaps the most fundamental aspect of Cascading Style Sheets is the cascade itself—
the process by which conflicting declarations are sorted out and from which the final
document presentation is determined. Integral to this process is the specificity of
selectors and their associated declarations, and the mechanism of inheritance.
Summary
|
111
CHAPTER 4
Values and Units
In this chapter, we’ll tackle features that are the basis for almost everything you can
do with CSS: the units that affect the colors, distances, and sizes of a whole host of
properties, as well as the units that help to define those values. Without units, you
couldn’t declare that an image should have 10 pixels of blank space around it, or that
a heading’s text should be a certain size. By understanding the concepts put forth
here, you’ll be able to learn and use the rest of CSS much more quickly.
Keywords, Strings, and Other Text Values
Everything in a stylesheet is text, but there are certain value types that directly repre‐
sent strings of text as opposed to, say, numbers or colors. Included in this category are
URLs and, interestingly enough, images.
Keywords
For those times when a value needs to be described with a word of some kind, there
are keywords. A very common example is the keyword none, which is distinct from 0
(zero). Thus, to remove the underline from links in an HTML document, you would
write:
a:link, a:visited {text-decoration: none;}
Similarly, if you want to force underlines on the links, then you would use the key‐
word underline.
If a property accepts keywords, then its keywords will be defined only for the scope of
that property. If two properties use the same word as a keyword, the behavior of the
keyword for one property will not necessarily be shared with the other. As an exam‐
113
ple, normal, as defined for letter-spacing, means something very different than the
normal defined for font-style.
Global keywords
CSS3 defines three “global” keywords that are accepted by every property in the spec‐
ification: inherit, initial, and unset.
inherit. The keyword inherit makes the value of a property on an element the same
as the value of that property on its parent element. In other words, it forces inheri‐
tance to occur even in situations where it would not normally operate. In many cases,
you don’t need to specify inheritance, since many properties inherit naturally. Never‐
theless, inherit can still be very useful.
For example, consider the following styles and markup:
#toolbar {background: blue; color: white;}
The div itself will have a blue background and a white foreground, but the links will
be styled according to the browser’s preference settings. They’ll most likely end up as
blue text on a blue background, with white vertical bars between them.
You could write a rule that explicitly sets the links in the “toolbar” to be white, but
you can make things a little more robust by using inherit. You just add the following
rule to the stylesheet:
#toolbar a {color: inherit;}
This will cause the links to use the inherited value of color in place of the user agent’s
default styles. Ordinarily, directly assigned styles override inherited styles, but
inherit can undo that behavior. It might not always be a good idea—for example,
here links might blend into surrounding text too much, and become an accessibility
concern—but it can be done.
Similarly, you can pull a property value down from a parent even if it wouldn’t hap‐
pen normally. Take border, for example, which is (rightfully) not inherited. If you
want a span to inherit the border of its parent, all you need is span {border:
inherit;}. More likely, though, you just want the border on a span to use the same
border color as its parent. In that case span {border-color: inherit;} will do the
trick.
114
|
Chapter 4: Values and Units
initial. The keyword initial sets the value of a property to the defined initial value,
which in a way means it “resets” the value. For example, the default value of fontweight is normal. Thus, declaring font-weight: initial is the same as declaring
font-weight: normal.
This might seem a little bit silly until you consider that not all values have explicitly
defined initial values. For example, the initial value for color is “depends on user
agent.” That’s not a funky keyword you should type! What it means is that the default
value of color depends on things like the preferences settings in a browser. While
almost nobody changes the default text color setting from black, someone might set it
to a dark gray or even a bright red. By declaring color: initial;, you’re telling the
browser to set the color of the element to whatever the user’s default color is set to be.
unset. The keyword unset acts as a universal stand-in for both inherit and ini
tial. If the property is inherited, then unset has the same effect as if inherit was
used. If the property is not inherited, then unset has the same effect as if initial was
used.
As of late 2017, Opera Mini did not support any of initial,
inherit, or unset. Internet Explorer did not support them through
IE11.
These global values are usable on all properties, but there is a special property that
only accepts the global keywords: all.
all
Values
inherit | initial | unset
Initial value See individual properties
all is a stand-in for all properties except direction and unicode-bidi. Thus, if you
declare all: inherit on an element, you’re saying that you want all properties
except direction and unicode-bidi to inherit their values from the element’s parent.
Consider the following:
section {color: white; background: black; font-weight: bold;}
#example {all: inherit;}
Keywords, Strings, and Other Text Values
|
115
This is a div.
You might think this causes the div element to inherit the values of color, back
ground, and font-weight from the section element. And it does do that, yes—but it
will also force inheritance of the values of every single other property in CSS (minus
the two exceptions) from the section element.
Maybe that’s what you want, in which case, great. But if you just want to inherit the
property values you wrote out for the section element, then the CSS would need to
look more like this:
section {color: white; background: black; font-weight: bold;}
#example {color: inherit; background: inherit; font-weight: inherit;}
Odds are what you really want in these situations is all: unset, but your stylesheet
may vary.
As of late 2017, a new global keyword, revert, was being consid‐
ered for adoption. Its goal was to allow rollbacks of values to those
set by other origins—for example, to let an author say, “All property
values for this element should be as if the author styles don’t exist,
but user agent and user styles do.” Since it was still under consider‐
ation, it has not been documented in detail here.
As of late 2017, Opera Mini and Microsoft Edge did not support
all. Support was under consideration for Edge.
Strings
A string value is an arbitrary sequence of characters wrapped in either single or dou‐
ble quotes, and is represented in value definitions with . Two simple exam‐
ples:
"I like to play with strings."
'Strings are fun to play with.'
Note that the quotes balance, which is to say that you always start and end with the
same kind of quotes. Getting this wrong can lead to all kinds of parsing problems,
since starting with one kind of quote and trying to end with the other means the
string won’t actually be terminated. You could accidentally incorporate subsequent
rules into the string that way!
If you want to put quote marks inside strings, that’s OK, as long as they’re either not
the kind you used to enclose the string or are escaped using a backslash:
116
|
Chapter 4: Values and Units
"I've always liked to play with strings."
'He said to me, "I like to play with strings."'
"It's been said that \"haste makes waste.\""
'There\'s never been a "string theory" that I\'ve liked.'
Note that the only acceptable string delimiters are ' and ", sometimes called “straight
quotes.” That means you can’t use “curly” or “smart” quotes to begin or end a string
value. You can use them inside a string value, as in this code example, though, and
they don’t have to be escaped:
"It’s been said that “haste makes waste.”"
'There’s never been a “string theory” that I’ve liked.'
This requires that you use Unicode encoding for your documents, but you should be
doing that regardless. (You can find the Unicode standard at http://www.unicode.org/
standard/standard.html.)
If you have some reason to include a newline in your string value, you can do that by
escaping the newline itself. CSS will then remove it, making things as if it had never
been there. Thus, the following two string values are identical from a CSS point of
view:
"This is the right place \
for a newline."
"This is the right place for a newline."
If, on the other hand, you actually want a string value that includes a newline charac‐
ter, then use the Unicode reference \A where you want the newline to occur:
"This is a better place \Afor a newline."
URLs
If you’ve written web pages, you’re almost certainly familiar with URLs (or, as in
CSS2.1, URIs). Whenever you need to refer to one—as in the @import statement,
which is used when importing an external stylesheet—the general format is:
url(protocol://server/pathname)
This example defines what is known as an absolute URL. By absolute, I mean a URL
that will work no matter where (or rather, in what page) it’s found, because it defines
an absolute location in web space. Let’s say that you have a server called web.waf‐
fles.org. On that server, there is a directory called pix, and in this directory is an image
waffle22.gif. In this case, the absolute URL of that image would be:
web.waffles.org/pix/waffle22.gif
This URL is valid no matter where it is found, whether the page that contains it is
located on the server web.waffles.org or web.pancakes.com.
Keywords, Strings, and Other Text Values
|
117
The other type of URL is a relative URL, so named because it specifies a location that
is relative to the document that uses it. If you’re referring to a relative location, such
as a file in the same directory as your web page, then the general format is:
url(pathname)
This works only if the image is on the same server as the page that contains the URL.
For argument’s sake, assume that you have a web page located at http://
web.waffles.org/syrup.html and that you want the image waffle22.gif to appear on this
page. In that case, the URL would be:
pix/waffle22.gif
This path works because the web browser knows that it should start with the place it
found the web document and then add the relative URL. In this case, the pathname
pix/waffle22.gif added to the server name http://web.waffles.org equals http://
web.waffles.org/pix/waffle22.gif. You can almost always use an absolute URL in place
of a relative URL; it doesn’t matter which you use, as long as it defines a valid loca‐
tion.
In CSS, relative URLs are relative to the stylesheet itself, not to the HTML document
that uses the stylesheet. For example, you may have an external stylesheet that
imports another stylesheet. If you use a relative URL to import the second stylesheet,
it must be relative to the first stylesheet.
As an example, consider an HTML document at http://web.waffles.org/toppings/
tips.html, which has a link to the stylesheet http://web.waffles.org/styles/basic.css:
Inside the file basic.css is an @import statement referring to another stylesheet:
@import url(special/toppings.css);
This @import will cause the browser to look for the stylesheet at http://
web.waffles.org/styles/special/toppings.css, not at http://web.waffles.org/toppings/
special/toppings.css. If you have a stylesheet at the latter location, then the @import in
basic.css should read one of the two following ways:
@import url(http://web.waffles.org/toppings/special/toppings.css);
@import url(../special/toppings.css);
Note that there cannot be a space between the url and the opening parenthesis:
body {background: url(http://www.pix.web/picture1.jpg);}
/* correct */
body {background: url (images/picture2.jpg);}
/* INCORRECT */
If the space is present, the entire declaration will be invalidated and thus ignored.
118
|
Chapter 4: Values and Units
Images
An image value is a reference to an image, as you might have guessed. Its syntax rep‐
resentation is .
At the most basic level of support, which is to say the one every CSS engine on the
planet would understand, an value is a value. In more advanced user
agents, stands for one of the following:
A URL identifier of an external resource; in this case, the URL of an image.
Perhaps unsurprisingly, a set of images, chosen based on a set of conditions
embedded into the value. For example, an image-set() could specify that a
larger image be used for desktop layouts, whereas a smaller image (both in pixel
size and file size) be used for a mobile design. It is intended to at least approxi‐
mate the behavior of the srcset attribute for picture elements. As of late 2016,
browser support for image-set was limited to Safari, Chrome, and desktop
Opera, and was not on par with srcset’s full range of capabilities.
Refers to either a linear or radial gradient image, either singly or in a repeating
pattern. Gradients are fairly complex, and thus are covered in detail in Chapter 9.
Identifiers
There are a few properties that accept an identifier value, which is a user-defined
identifier of some kind; the most common example is generated list counters. They
are represented in the value syntax as . Identifiers themselves are words,
and are case-sensitive; thus, myID and MyID are, as far as CSS is concerned, completely
distinct and unrelated to each other. In cases where a property accepts both an identi‐
fier and one or more keywords, the author should take care to never define an identi‐
fier identical to a valid keyword.
Numbers and Percentages
These value types are special because they serve as the foundation for so many other
values types. For example, font sizes can be defined using the em identifier (covered
later in this text) preceded by a number. But what kind of number? Defining the types
of numbers here lets us speak clearly later on.
Numbers and Percentages
|
119
Integers
An integer value is about as simple as it gets: one or more numbers, optionally pre‐
fixed by a + or − sign to indicate a positive or negative value. That’s it. Integer values
are represented in value syntax as . Examples include 13, −42, 712, and
1,066.
Integer values that fall outside a defined range are, by default, considered invalid and
cause the entire declaration to be ignored. However, some properties define behavior
that causes values outside the accepted range to be set to the accepted value closest to
the declared value, known as clamping. In cases (such as the property z-index) where
there is no restricted range, user agents must support values up to ±1,073,741,824
(±230).
Numbers
A number value is either an or a real number, which is to say an integer
followed by a dot and then some number of following integers. Additionally, it can be
prefixed by either + or − to indicate positive or negative values. Number values are
represented in value syntax as . Examples include 2.7183, −3.1416, and
6.2832.
The reason a can be an and yet there are separate value types is
that some properties will only accept integers (e.g., z-index), whereas others will
accept any real number (e.g., flex-grow). As with integer values, number values may
have limits imposed on them by a property definition; for example, opacity restricts
its value to be any valid in the range 0 to 1, inclusive. By default, number
values that fall outside a defined range are, by default, considered invalid and cause
the entire declaration to be ignored. However, some properties define behavior that
causes values outside the accepted range to be set to the accepted value closest to the
declared value (generally referred to as “clamping”).
Percentages
A percentage value is a followed by a percentage sign (%), and is repre‐
sented in value syntax as . Examples would include 50% and 33.333%.
Percentage values are always relative to another value, which can be anything—the
value of another property of the same element, a value inherited from the parent ele‐
ment, or a value of an ancestor element. Any property that accepts percentage values
will define any restrictions on the range of allowed percentage values, and will also
define the way in which the percentage is relatively calculated.
120
|
Chapter 4: Values and Units
Fractions
A fraction value (or flex value) is a followed by the label fr. Thus, one frac‐
tional unit is 1fr. This is a concept introduced by Grid Layout, and is used to divide
up fractions of the unconstrained space in a layout. See Chapter 13 for more details.
Distances
Many CSS properties, such as margins, depend on length measurements to properly
display various page elements. It’s likely no surprise, then, that there are a number of
ways to measure length in CSS.
All length units can be expressed as either positive or negative numbers followed by a
label, although note that some properties will accept only positive numbers. You can
also use real numbers—that is, numbers with decimal fractions, such as 10.5 or 4.561.
All length units are followed by short abbreviation (usually two characters) that rep‐
resents the actual unit of length being specified, such as in (inches) or pt (points).
The only exception to this rule is a length of 0 (zero), which need not be followed by a
unit when describing lengths.
These length units are divided into two types: absolute length units and relative length
units.
Absolute Length Units
We’ll start with absolute units because they’re easiest to understand, despite the fact
that they’re almost unusable in regular web design. The six types of absolute units are
as follows:
Inches (in)
As you might expect, this notation refers to the inches you’d find on a ruler in the
United States. (The fact that this unit is in the specification, even though almost
the entire world uses the metric system, is an interesting insight into the perva‐
siveness of US interests on the internet—but let’s not get into virtual sociopoliti‐
cal theory right now.)
Centimeters (cm)
Refers to the centimeters that you’d find on rulers the world over. There are 2.54
centimeters to an inch, and one centimeter equals 0.394 inches.
Millimeters (mm)
For those Americans who are metric-challenged, there are 10 millimeters to a
centimeter, so an inch equals 25.4 millimeters, and a millimeter equals 0.0394
inches.
Distances
|
121
Quarter-millimeters (q)
There are 40 Q units in a centimeter; thus, setting an element to be 1/10 of a cen‐
timeter wide—which is also to say, a millimeter wide—would mean a value of 4q.
(Only Firefox supported q as of late 2016.)
Points (pt)
Points are standard typographical measurements that have been used by printers
and typesetters for decades and by word processing programs for many years.
Traditionally, there are 72 points to an inch (points were defined before wide‐
spread use of the metric system). Therefore the capital letters of text set to 12
points should be one-sixth of an inch tall. For example, p {font-size: 18pt;} is
equivalent to p {font-size: 0.25in;}.
Picas (pc)
Picas are another typographical term. A pica is equivalent to 12 points, which
means there are 6 picas to an inch. As just shown, the capital letters of text set to
1 pica should be one-sixth of an inch tall. For example, p {font-size: 1.5pc;}
would set text to the same size as the example declarations found in the defini‐
tion of points.
Pixels (px)
A pixel is a small box on screen, but CSS defines pixels more abstractly. In CSS
terms, a pixel is defined to be the size required to yield 96 pixels per inch. Many
user agents ignore this definition in favor of simply addressing the pixels on the
screen. Scaling factors are brought into play when page zooming or printing,
where an element 100px wide can be rendered more than 100 device dots wide.
These units are really useful only if the browser knows all the details of the screen on
which your page is displayed, the printer you’re using, or whatever other user agent
might apply. On a web browser, display is affected by the size of the screen and the
resolution to which the screen is set—and there isn’t much that you, as the author,
can do about these factors. You can only hope that, if nothing else, the measurements
will be consistent in relation to each other—that is, that a setting of 1.0in will be
twice as large as 0.5in, as shown in Figure 4-1.
Figure 4-1. Setting absolute-length left margins
Nevertheless, despite all that, let’s make the highly suspect assumption that your com‐
puter knows enough about its display system to accurately reproduce real-world
measurements. In that case, you could make sure every paragraph has a top margin of
122
|
Chapter 4: Values and Units
half an inch by declaring p {margin-top: 0.5in;}. Regardless of font size or any
other circumstances, a paragraph will have a half-inch top margin.
Absolute units are much more useful in defining stylesheets for printed documents,
where measuring things in terms of inches, points, and picas is much more common.
Pixel lengths
On the face of things, pixels are straightforward. If you look at a screen closely
enough, you can see that it’s broken up into a grid of tiny little boxes. Each box is a
pixel. If you define an element to be a certain number of pixels tall and wide, as in the
following markup:
The following image is 20 pixels tall and wide:
then it follows that the element will be that many screen elements tall and wide, as
shown in Figure 4-2.
Figure 4-2. Using pixel lengths
In general, if you declare something like font-size: 18px, a web browser will almost
certainly use actual pixels on your screen—after all, they’re already there—but with
other display devices, like printers, the user agent will have to rescale pixel lengths to
something more sensible. In other words, the printing code has to figure out how
many dots there are in a pixel.
On the other hand, pixel measurements are often useful for expressing the size of
images, which are already a certain number of pixels tall and wide. These days,
responsive design means that we often want to express image size in relation to the
size of the text of the width of the viewport, regardless of the number of actual pixels
in the image. You do end up relying on the image-scaling routines in user agents, but
those have been getting pretty good. Scaling of images really makes sense with vectorbased images like SVG.
Pixel theory
In its discussion of pixels, the CSS specification recommends that, in cases where a
display’s resolution density is significantly different than 96 pixels per inch (ppi), user
agents should scale pixel measurements to a “reference pixel.” CSS2 recommended 90
ppi as the reference pixel, but CSS2.1 and CSS3 recommend 96 ppi. The most com‐
mon example is a printer, which has dots instead of pixels, and which has a lot more
Distances
|
123
dots per inch than 96! In printing web content, then, it may assume 96 pixels per inch
and scale its output accordingly.
If a display’s resolution is set to 1,024 pixels wide by 768 pixels tall, its screen size is
exactly 10 2/3 inches wide by 8 inches tall, and the screen it is filled entirely by the
display pixels, then each pixel will be 1/96 of an inch wide and tall. As you might
guess, this scenario is a fairly rare occurrence. So, on most displays, the actual num‐
ber of pixels per inch (ppi) is higher than 96—sometimes much higher. The Retina
display on an iPhone 4S, for example, is 326 ppi; the display on the iPad 264 ppi.
As a Windows XP user, you should be able to set your display
driver to make the display of elements correspond correctly to realworld measurements. The path to the ruler dialog is Start→Control
Panel; double-click Display; click the Settings tab; then click
Advanced to reveal a dialog box (which may differ on each PC).
You should see a dropdown or other form control labeled Font
Size; select Other.
Resolution Units
With the advent of media queries and responsive designs, three new unit types were
introduced in order to be able to describe display resolution:
Dots per inch (dpi)
The number of display dots per linear inch. This can refer to the dots in a paper
printer’s output, the physical pixels in an LED screen or other device, or the ele‐
ments in an e-ink display such as that used by a Kindle.
Dots per centimeter (dpcm)
Same as dpi, except the linear measure is one centimeter instead of one inch.
Dots per pixel unit (dppx)
The number of display dots per CSS px unit. As of CSS3, 1dppx is equivalent to
96dpi because CSS defines pixel units at that ratio. Just bear in mind that ratio
could change in future versions of CSS.
As of late 2017, these units are only used in the context of media queries. As an exam‐
ple, an author can create a media block to be used only on displays that have higher
than 500 dpi:
@media (min-resolution: 500dpi) {
/* rules go here */
}
124
|
Chapter 4: Values and Units
Relative Length Units
Relative units are so called because they are measured in relation to other things. The
actual (or absolute) distance they measure can change due to factors beyond their
control, such as screen resolution, the width of the viewing area, the user’s preference
settings, and a whole host of other things. In addition, for some relative units, their
size is almost always relative to the element that uses them and will thus change from
element to element.
em and ex units
First, let’s consider em and ex, which are closely related. In CSS, one “em” is defined to
be the value of font-size for a given font. If the font-size of an element is 14 pixels,
then for that element, 1em is equal to 14 pixels.
As you may suspect, this value can change from element to element. For example, let’s
say you have an h1 with a font size of 24 pixels, an h2 element with a font size of 18
pixels, and a paragraph with a font size of 12 pixels. If you set the left margin of all
three at 1em, they will have left margins of 24 pixels, 18 pixels, and 12 pixels, respec‐
tively:
h1 {font-size: 24px;}
h2 {font-size: 18px;}
p {font-size: 12px;}
h1, h2, p {margin-left: 1em;}
small {font-size: 0.8em;}
Left margin = 24 pixels
Left margin = 18 pixels
Left margin = 12 pixels
When setting the size of the font, on the other hand, the value of em is relative to the
font size of the parent element, as illustrated by Figure 4-3.
Figure 4-3. Using em for margins and font sizing
In theory, one em is equal to the width of a lowercase m in the font used—that’s
where the name comes from, in fact. It’s an old typographer’s term. However, this is
not assured in CSS.
ex, on the other hand, refers to the height of a lowercase x in the font being used.
Therefore, if you have two paragraphs in which the text is 24 points in size, but each
paragraph uses a different font, then the value of ex could be different for each
Distances
|
125
paragraph. This is because different fonts have different heights for x, as you can see
in Figure 4-4. Even though the examples use 24-point text—and therefore each exam‐
ple’s em value is 24 points—the x-height for each is different.
Figure 4-4. Varying x heights
The rem unit
Like the em unit, the rem unit is based on declared font size. The difference—and it’s a
doozy—is that whereas em is calculated using the font size of the element to which it’s
applied, rem is always calculated using the root element. In HTML, that’s the html
element. Thus, declaring any element to have font-size: 1rem; is setting it to have
the same font-size value as the root element of the document.
As an example, consider the following markup fragment. It will have the result shown
in Figure 4-5.
This paragraph has
inheritance.
1em;">This paragraph has the same font size as its parent
1rem;">This paragraph has the same font size as the root
In effect, rem acts as a reset for font size: no matter what relative font sizing has hap‐
pened to the ancestors of an element, giving it font-size: 1rem; will put it right
back where the root element is set. This will usually be the user’s default font size,
unless you (or the user) have set the root element to a specific font size.
For example, given this declaration, 1rem will always be equivalent to 13px:
html {font-size: 13px;}
However, given this declaration, 1rem will always be equivalent to three-quarters the
user’s default font size:
html {font-size: 75%;}
In this case, if the user’s default is 16 pixels, then 1rem will equal 12px. If the user has
actually set their default to 12 pixels—a few people do this—then 1rem will equal 9px;
if the default setting is 20 pixels, then 1rem equals 15px. And so on.
You are not restricted to the value 1rem. Any real number can be used, just as with the
em unit, so you can do fun things like set all of your headings to be multiples of the
root element’s font size:
h1
h2
h3
h4
h5
h6
{font-size:
{font-size:
{font-size:
{font-size:
{font-size:
{font-size:
2rem;}
1.75rem;}
1.4rem;}
1.1rem;}
1rem;}
0.8rem;}
In browsers that support the keyword initial, font-size: 1rem
is equivalent to font-size: initial as long as no font size is set
for the root element.
The ch unit
An interesting addition to CSS3 is the ch unit, which is broadly meant to represent
“one character.” The way it is defined in CSS3 is:
Equal to the advance measure of the “0” (ZERO, U+0030) glyph found in the font used
to render it.
The term advance measure is actually a CSS-ism that corresponds to the term
“advance width” in font typography. CSS uses the term “measure” because some
scripts are not right to left or left to right, but instead top to bottom or bottom to top,
and so may have an advance height rather than an advance width. For simplicity’s
sake, we’ll stick to advance widths in this section.
Distances
|
127
Without getting into too many details, a character glyph’s advance width is the dis‐
tance from the start of a character glyph to the start of the next. This generally corre‐
sponds to the width of the glyph itself plus any built-in spacing to the sides.
(Although that built-in spacing can be either positive or negative.)
CSS pins the ch unit to the advance width of a zero in a given font. This is in parallel
to the way that em is calculated with respect to the font-size value of an element.
The easiest way to demonstrate this unit is to run a bunch of zeroes together and then
set an image to have a width with the same number of ch units as the number of
zeroes, as shown in Figure 4-6:
img {height: 1em; width: 25ch;}
Figure 4-6. Character-relative sizing
Given a monospace font, all characters are by definition 1ch wide. In any propor‐
tional face type, which is what the vast majority of Western typefaces are, characters
may be wider or narrower than the “0” and so cannot be assumed to be 1ch wide.
As of late 2017, only Opera Mini and Internet Explorer had prob‐
lems with ch. In IE11, ch was mis-measured to be exactly the width
of the “0” glyph, not the glyph plus the small amount of space to
either side of it. Thus, 5ch was less than the width of “00000” in
IE11. This error was corrected in Edge.
128
|
Chapter 4: Values and Units
Viewport-relative units
Another new addition in CSS3 are the three viewport-relative size units. These are
calculated with respect to the size of the viewport—browser window, printable area,
mobile device display, etc.:
Viewport width unit (vw)
This unit is calculated with respect to the viewport’s width, which is divided by
100. Therefore, if the viewport is 937 pixels wide, 1vw is equal to 9.37px. If the
viewport’s width changes, say by dragging the browser window wider or more
narrow, the value of vw changes along with it.
Viewport height unit (vh)
This unit is calculated with respect to the viewport’s height, which is divided by
100. Therefore, if the viewport is 650 pixels tall, 1vh is equal to 6.5px. If the view‐
port’s height changes, say by dragging the browser window taller or shorter, the
value of vh changes along with it.
Viewport minimum unit (vmin)
This unit is 1/100 of the viewport’s width or height, whichever is lesser. Thus,
given a viewport that is 937 pixels wide by 650 pixels tall, 1vmin is equal to 6.5px.
Viewport maximum unit (vmax)
This unit is 1/100 of the viewport’s width or height, whichever is greater. Thus,
given a viewport that is 937 pixels wide by 650 pixels tall, 1vmax is equal to
9.37px.
Note that these are length units like any other, and so can be used anywhere a length
unit is permitted. You can scale the font size of a heading in terms of the viewport,
height, for example, with something like h1 {font-size: 10vh;}. This sets the font
size to be 1/10 the height of the viewport—a technique potentially useful for article
titles and the like.
These units can be particularly handy for creating full-viewport interfaces, such as
those one would expect to find on a mobile device, because it can allow elements to
be sized compared to the viewport and not any of the elements within the document
tree. It’s thus very simple to fill up the entire viewport, or at least major portions of it,
and not have to worry about the precise dimensions of the actual viewport in any
particular case.
Here’s a very basic example of viewport-relative sizing, which is illustrated in
Figure 4-7:
div {width: 50vh; height: 33vw; background: gray;}
Distances
|
129
An interesting (though perhaps not useful) fact about these units is that they aren’t
bound to their own primary axis. Thus, for example, you can declare width: 25vh;
to make an element as wide as one-quarter the height of the viewport.
As of late 2016, viewport-relative units were supported by all
browsers except Opera Mini, plus the odd exception that vmax is
not supported in Microsoft browsers.
Figure 4-7. Viewport-relative sizing
Calculation values
In situations where you need to do a little math, CSS provides a calc() value. Inside
the parentheses, you can construct simple mathematical expressions. The permitted
operators are + (addition), - (subtraction), * (multiplcation), and / (division), as well
as parentheses. These follow the traditional PEMDAS (parentheses, exponents, multi‐
plication, division, addition, subtraction) precedence order, although in this case it’s
really just PMDAS since exponents are not permitted in calc().
Support for parentheses in calc() appears to be a convenience
provided by browsers, since they’re not mentioned in the syntax
definition for calc(). It seems likely that support for parentheses
will remain, but use at your own risk.
As an example, suppose you want your paragraphs to have a width that’s 2 em less
than 90% the width of their parent element. Here’s how you express that with calc():
130
|
Chapter 4: Values and Units
p {width: calc(90% - 2em);}
calc() can be used anywhere one of the following value types is permitted: ,
, ,