DAN STORM

skip to main content skip to secondary navigation

CSS Tutorial

CSS2.1 Pseudo-elements, Generated content, and Pseudo-classes

CSS2.1 Pseudo-elements

before and after

Two pseudo-elements introduced in CSS2.1 are before and after. As their names imply, they insert content before and after elements. The content can be a string.

h1::before {content: "Hello World";} li::before {content: ">>";}

Using these pseudo-elements helps ensure visual uniformity in your site, and allows you to design those aspects of HTML display that you might otherwise not be able to exploit. These are classified as pseudo-elements because they apply to only part of an element. We will look more closely at these two pseudo-elements in the next section.

A copyright notice could be appended to each page.

body::after {content: "Copyright 2009 by Joe Smith";}

Some disapprove of the content property with the before and after pseudo elements because content is in the CSS, not in the HTML where it should be. This is a good point. Fortunately there are other uses for these pseudo-elements.

Generated Content

Content Property: the url() and attr() Functions

As we saw above, the content property can call literal strings. This property also has two functions that let you manipulate more than literal strings. The url() function lets you append an image or media file before or after an element.

q::before {content: url(images/quote.gif);} em::before {content: url("ding.wav");}

You could insert an image or text before (or after) every visited link in conjunction with the change in color of a visited link, to reinforce where a user has been.

a:visited::before {content: url(images/checkmark.gif);}

The attr() function is used to display the contents of a tag's attribute. For example, the contents of the title attribute could be displayed after an image.

img::after {content: attr(title);}

This is to be distinguished from the simple attribute selector mentioned earlier. The following declaration styles the IMG element if there is a title attribute. It does not display, much less style, the title attribute itself.

img[title] {color: red;}
Counter-increment and Counter-reset Properties

An important use of these pseudo-elements is to create counters for headings and subheadings. For example, you could put all chapter headings in H1 elements, and subheadings in H2 elements, and then have the CSS insert some content before every occurrence, incrementing the numbers automatically. Like a word processor, the numbers will re-order if an item is added, deleted, or its display is set to "none". Let's analyze the CSS to accomplish this.

h1::before {content: counter(section);}

Instead of being a literal string, or an attribute like title, the content property refers to the "counter" function, which is "section" in this example. This means that before every H1 element you want an incremental counter, and you are designating it as the section. This will place a number in front of all the H1 content. Now let's add another piece to our example.

h1::before {content: "Section " counter(section) " ";}

The content property is more complex than some other properties in that it can take a value and a function. In the example above a literal string "Section "—note the space before the close quote—is designated to appear before the content of the H1 element, and the "counter" function is added, as we've already seen. This places content and a number in front of H1 content. Note also the optional punctuation and space between the quotes at the end of the declaration. But now we need to increment the number.

h1::before {content: "Section " counter(section) ": "; counter-increment: section;}

Now let's assume you have the following HTML.

<h1>The Civil War</h1> ..... <h1>World War I</h1>

Output would look something like this.

Section 1: The Civil War ..... Section 2: World War I

The counter-increment property here indicates that sections which have been assigned to H1 elements should be incremented. The counter-increment property indicates the increment step, which can be negative, positive, or zero, but defaults to 1. If you had wanted to increment by two, for, example, you would rewrite the counter-increment as follows.

counter-increment: section 2;

Also, note that the counter will increment if an element's visibility is "hidden", but not if its display is set to "none". Now let's assume that you want to increment sections and subsections alike.

h1::before {content: "Section " counter(section) ". "; counter-increment: section; counter-reset: sub-section;} h2::before {content: counter(section) "." counter(sub-section) " "; counter-increment: sub-section;}

We are saying that H1 elements should be prefixed with the word "Section ", followed by a period. Then the section is to be incremented by 1. Note that we have added the counter-reset property. This directs the browser to reset the subsection with every new section. If we leave it out the subsections will continue to increment. Before H2 elements we have not indicated a literal string. Instead, we prefix the section number (from the H1 element), followed by a period. After the period we call the sub-section. In the counter-increment property we indicate that this subsection should increment. Now let's assume you have the following HTML.

<h1>The Civil War</h1> <h2>Famous Battles</h2> ... <h1>World War I</h1> <h2>Famous Battles</h2>

Output would look something like this.

Section 1. The Civil War
1.1 Famous Battles
...
Section 2. World War I
2.1 Famous Battles

Note that you can reset more than one value at a time as follows.

h1 {counter-reset: section -1 imagenum 99;}
Content Property: the counter() and counters() Functions

In the previous examples we saw that the content property takes the counter() function. In that function we called the name of the section: "section". This function can also call a list style type.

counter(name, style)

Among these styles are "disc", "circle", "square", "decimal", "lower-roman", and "upper-roman", though "decimal" is the default. For a complete list see the appendix. We could thus re-write our earlier H2 style, now designating that the counter prefix the subsection with a circle.

h2::before {content: counter(section, circle) "." counter(sub-section) " "; counter-increment: sub-section;}

The counters() function has the following form.

counters(name, string, style)

To refer to a sequence of nested counters of the same name you can use the counters() function. "style" is optional. This operates in an outermost to innermost direction separated by the specified string. The counters are rendered in the indicated style, with decimal as the default.

ol {counter-reset: item;} li {display: block;} li::before {content: counters(item, ".") " "; counter-increment: item;}

Other values of the content property are "open-quote", "close-quote", "no-open-quote", and "no-close-quote". With the before and after pseudo-elements you can surround certain elements with quotes.

h1::before {content: open-quote;} h1::after {content: close-quote;}

You can combine values too. Here we add quotes to the incrementing.

h1::before {content: "Section " counter(section) ". " open-quote; counter-increment: section; counter-reset: sub-section;} h1::after {content: close-quote;}

CSS2.1 Pseudo-classes

focus

The focus pseudo-class is used to add style to an element when it has focus. A common use is to change the appearance of a form field when the user is entering input.

input[type="text"]:focus {background-color: #999; outline: 1px solid #F00;}

When a user selects a field the background color will turn gray, and the field will be outlined in red. If you've been paying attention you will know that this previous example does not work in IE6 since it does not support either the attribute selector or the outline property. Furthermore, IE6 does not even support the focus pseudo-class. IE6 is totally at a loss here.

This brings up a point. How can we take advantage of all CSS if a popular browser does not support it? The answer is that you can save these extras for non-essential designs. Another way to put it is that we should not rely on unsupported CSS for essential design.

lang()

The lang() pseudo-class allows the author to specify a language to use in a specified element. A two-letter language code is placed in the parentheses, and can be further defined by a hyphen and a subclass of the language. For example, "en-US" is American English.

p:lang(en) {color: green;} p:lang(de) {color: purple;}

This pseudo-class is thus useful because punctuation varies from one language to another. The quotes property can be used alone or in tandem with the before and after pseudo-elements. In tandem with the lang() pseudo-class, this property allows you to specify different kinds of quotes based on the target language. In the parentheses you specify the language code, and then within 4 sets of double quotes, you specify the following from left to right.

opening quotation mark || closing quotation mark || opening inner quotation mark || closing inner quotation mark q:lang(en) {quotes: '"' '"' "'" "'";}

This tells your browser how to display nested quotes for English. The following would be displayed as "This is a 'big' quote".

<html lang="en"> ... <p><q>This is a <q>big</q> quote</q></p>

If we wanted to apply quotes for Norwegian, for example, we would use the following.

q:lang(no) {quotes: """ """ "'" "'";}

To exercise a little more control, we can use the before and after pseudo-elements and globally enclose quotations using descriptive quotes.

q::before {content: double, left angle quote; color: red;} q::after {content: double, right angle quote; color: red;}

These quote values can consist of the actual quotes, entity numbers (e.g. &#8250;), or descriptive names, as follows.

  • double quote
  • single quote
  • single, left angle quote
  • single, right angle quote
  • double, left angle quote
  • double, right angle quote
  • left quote (single high-6)
  • right quote (single high-9)
  • left quote (double high-6)
  • right quote (double high-9)
  • double quote (double low-9)
first-child

We said in our initial definition of pseudo-classes that there are two types. CSS1 pseudo-classes act upon an entire element, but are state-oriented. The other kind of pseudo-class is stateless. Let's say that you want to act upon the first child of several children rather than on all. There is no way to do that in CSS1 without using a class or inline style. It so happens that there is a first-child pseudo-class. Although it can be deduced from the document tree, it cannot be accessed explicitly by a selector because it refers to a particular, rather than a repeat, occurrence of an element. Consider the following selector.

div p span {font-size: 12px;}

In this example we give the SPAN element a font-size of 12 pixels. If there were only one SPAN element there would be no need for a pseudo-class to access that element. But if there are several sibling SPAN elements, and you only want to apply the class to the first one, you would use the pseudo-class first-child.

div p span:first-child {font-size: 12px;}

Again, though the first SPAN element lies within the document tree, the means of accessing it and excluding all other SPAN elements at that level lie outside of the control of a selector. If, for example, you have an unordered list, and you want only the first list item to be bold, you could place this in your CSS.

ul li:first-child {font-size: bold;}

Note that first-child here refers to a list item which is itself a first child of its parent, the unordered list. It does not refer to a child of the list item. This is a useful pseudo-class because you can change a style without adding a class to your CSS or editing the HTML. Note that IE6 does not support this pseudo-class.

To put this pseudo-class to work, we again return to our cart table example from previous lessons. As you may recall, we first defined all TD elements within a cart table to be center-aligned, and then created a class to make some cells left-aligned.

table#cartTable td {text-align: center; padding: 8px 0;} table#cartTable td.qty {text-align: left; padding-left: 6px;}

Then we introduced the sibling selector so that we could dispense with the class, and specifically make the second cell left-aligned.

table#cartTable td {text-align: center; padding: 8px 0;} table#cartTable td + td {text-align: left; padding-left: 6px;} table#cartTable td + td + td {text-align: center; padding-left: 6px;}

But we were not happy with this method because we had to then turn around and cancel the second declaration with a third declaration. Using the first-child pseudo-class and the sibling selector together we can dispense with the class without having to reset a declaration.

table#cartTable td {text-align: center; padding: 8px 0;} table#cartTable td:first-child + td {text-align: left; padding-left: 6px;}

The second declaration styles a TD element that is a sibling of a first child TD element when they both share the same parent (the TABLE element) with the ID "cartTable". If you wanted to affect the third cell you could add another "+ td". What if you want to act upon the last child, or the nth child? That will have to wait for CSS3 support, which we cover below. In sum, we have been introduced two kinds of pseudo-classes: those that affect a state of an element, and those that find particular elements within the document tree.

CSS2.1 System Colors

Under CSS2 developers can assign styles that correspond to the user's graphic environment. The following example would assign P elements the same color as the user's WindowText color, and the background color to match their Window color. In other words, web developers can set color preferences already adopted by the end user.

p {color: WindowText; background-color: Window;}

This has been pulled from the CSS3 specification..