Overview of This Lesson

In addition to the selectors you've used so far, sometimes you'll want to create rules with complex selectors that select and style elements based on their state. For example, you might wish to style the last column in a table differently from the rest of the table cells, or you might want to style a button so that it changes its background and foreground colour when the user moves their mouse over it (and then change it back when the user moves their mouse away). You might want to style a list so that every other item has a background colour to make it easier to read, or you might want to style a form's input field to be red when the input is invalid.

Pre-Requisites

To get the most out of this lesson, you should make sure you've gone through the Combinators lesson and its pre-requisites. It's also important that you're familiar with the HTML DOM and related terminology.

Intro to Pseudo Classes

Pseudo classes allow you to change the styling of an element based on its state. The state of an element includes whether or not the user is clicking it or hovering their mouse over it, if the element has the focus (the cursor is focused on/in the element such that any key presses directly affect the element, e.g. when the cursor is flashing inside a form field), when a link is clicked/visited/hovered over, or even what position an element has in the DOM (e.g. is it the last child in a parent, the next sibling of an element, etc.)

We use a pseudo class in a selector by putting the name of the pseudo class, preceeded by a colon. The pseudo class can be part of a complex selector with a combinator or on its own after an element/type selector, class selector, or id selector. Examples:

/* style how links look when the user hovers over them */
a:hover { ... }

/* style the last element in a container with the .important class 
   assigned to it */
.important:last-child { ... }

/* style the first paragraph that's a child of an element 
that has the an id of "info" */
#info > p:first-of-type { ... }

A list of all the pseudo classes in the current version of CSS can be found in MDN: Pseudo Classes. We'll go over many of these in the following sections.

More on Hover

The hover pseudo class is not just for links. You can use hover on any element the user can pass their mouse over.

See the Pen Hover Pseudo Class by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the hover example CodePen, opens in the pen tab)

In the example above, a definition for a term is displayed in an <aside> element when the user hovers over the <div> that looks like a button. This is done using an adjacent sibling combinator and the :hover pseudo class: When the user hovers over the button, the <aside> directly below it appears (.button:hover + aside). Additionally, there's a style rule that changes the appearance of the div-button when the user hovers over it.

Document Structure

Part of an element's state includes where it sits at a given moment within the DOM. For example, you can style only the first or last occurrence of an element, or you can style every 3rd element or all even/odd elements.

Styling the First/Last Element in a Container

You can style the first or last child in a parent: For example, you might want to style the first paragraph of an article a certain way, or you might want to style the first article different from the rest of the articles to show that it's the newest one. Alternatively, you might want to style the last cell in each row of a table that shows a summary of the previous cells in that row, or the last row of the table if it shows the sums of the table columns.

The :first-child and :last-child pseudo classes will select the first or last child inside a parent element. For example, the following CodePen shows some styling on nested divs that can be improved upon by using the :last-child pseudo class:

See the Pen Pseudo Classes: last-child (no lists) by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the :last-child pseudo class demo CodePen, opens in the pen tab)

In this code, the CSS contains a rule using the selector div to style all DIV elements to have a border on all sides. We don't want the nested divs to have the same border, so a second rule with the selector div > div styles all DIV elements that are children of another DIV element: to have a different border (just a dashed bottom border line).

When you look at the results of this code that styles the DIV elements inside the parent DIV element, you can see that the bottom DIV element's border is very close to the outer DIV's border. This makes it look a little bit too "busy". We can fix this by turning off the border on the last inner DIV element. Uncomment the last rule inside the CSS tab to see it work:

div > div:last-child {
  border: none;
}

This turns off the border on the last DIV child of a parent DIV.

Note that if you instead used the selector div:last-child, this would style any DIV element that is the last child of any parent element: Since the outer <div> is inside a container (a <section> element), that outer <div> is also a "last child". If you change the selector of the last rule to div:last-child, you'll notice that the border on the outer div disappears! This would also happen if you added more DIV elements to the HTML: you will end up styling the last DIV in the <section> (or in every section, if you also added more sections).

For example, try adding this under the outer div, inside the <section>:

<h2>Suspense Novels: Genres</h2>
<div>
  <div>Legal Suspense</div>
  <div>Paranormal Thrillers</div>
  <div>Psychological Suspense</div>
  <div>Technological Thrillers</div>
</div>

After adding the additional HTML, you'll see that the second outer DIV containing the list of suspense novels loses it's border also, because it's the last child of the SECTION element.

Where the :last-child pseudo class selects the last of a specific type of element inside a parent container, the first-child pseudo class selects the first of a specific type of element inside a parent container.

For example, if you want the first paragraph in an article to have a certain style, you can use first-child:

See the Pen Pseudo Classes: first-child by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the descendant combinator exercise CodePen, opens in the pen tab)

In this example, there are a few paragraphs inside containers (two of the parent containers are <div> elements and two are <aside> elements). Each paragraph that is the first child of its container is styled in a different way.

Note that in the last container, the paragraph is not styled! This is because the paragraph is NOT the first child of its parent! In this case, the parent is an <aside> and the first child is an <h1>, not a <p> element. The selector p:first-child will only select and style a <p> element if it is the first child of the parent container.

But what if you wanted to style all "first" paragraphs, even if they're not the first child in the parent container? There are also two pseudo-classes that will allow you to select the first or last of a specific type of element even if it's not the first or last child element: :first-of-type will select the first occurrence of a specific child element, even if it's not the first child, and :last-of-type will select the last occurrence of a specific child element, even if it's not the last child.

In this codepen, there are several <article> elements with level-2 headings as the first child (and one article without a heading, which is makes the HTML invalid, but I did it only for demonstration purposes). There are also several paragraphs in each article. Also, the first article has an <aside> element that contains 3 paragraphs.

See the Pen Pseudo Classes: first-child by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the descendant combinator exercise CodePen, opens in the pen tab)

Notice how the third rule uses the selector p:first-child: this styles the first <p> in any parent container, as long as that <p> is the first child. This results in none of the first paragraphs being styled (except the one in the <aside>, which we actually don't want) in articles where there is a proper level-heading: the first and last article's first paragraphs are not styled at all. Only the first paragraph in the second article is styled, because it is the first child of that <article> element.

To have every first-paragraph of each article styled, you can change the selector to p:first-of-type. This will style the first occurrence of a <p> element in every parent container, even if it's not that container's first child. Try it, and see how it works.

What about the paragraph in the <aside> element? It is styled because it's the first of its type in the parent, but we don't want paragraphs inside <aside> elements to be styled. How can we fix this? I gave you a hint in the CSS tab of the CodePen, but if you get stuck, the solution is given and explained in a comment near the bottom of the CSS.

Here's another Codepen that shows you the difference between :first-child and :first-of-type, and between :last-child and :last-of-type.

See the Pen CSS Pseudo Class: first/last of type by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the first/last child/of-type demo CodePen, opens in the pen tab)

In this example, you'll see in the HTML there are 2 divs. Each div has some child elements: the first one has a heading, some paragraphs, and a span; The second div has some paragraphs only.

In the CSS tab, you'll see there are selectors p:first-child (styles red text) and p:first-of-type (styles underline). Notice that in the first div, the first paragraph has the underline but not the red text. That's because the p:first-child selector only selects paragraph elements that are the first child in the container.

In the second div, the first paragraph has both underline and red text: here, the paragraph IS the first child in the div, so it gets the red text. :first-child only applies if the element is the first child in the parent container, but :first-of-type looks for the first occurrence of the element in the parent container.

Similarly, the last paragraph in the first div only gets the strikethrough style from p:last-of-type because it's the last paragraph element in the div, but it doesn't get the purple colour from p:last-child because that paragraph element is not the last child in the div. In the second div, the last paragraph element IS the last child so it gets the purple text and the strikethrough.

Descendant Combinator and First/Last Child Pseudo Class

What if you want to style the first or last element of a parent container, regardless of the element type? For example, say you have various <article> and <section> elements and each has a specific level-N heading as the first child, but the headings are all different types (h1, h2, h3, etc)? You can use the descendant combinator (the space) combined with :first-child to style all the elements that are the first child of an article or section, regardless of the element type. In this example, I styled the first child element of all article and section elements to have a different colour text:

See the Pen Pseudo Classes: first-child vs first-of-type by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the descendant combinator with first/last-child demo CodePen, opens in the pen tab)

In this example, we are able to change the colour of all the first-child headings, even though the actual heading elements were different. There is one issue with the one section that has no heading (which is actually invalid HTML), but I put some notes in the comments about how you could fix that - it's a more advanced solution, but very cool.

Styling the Nth Element in a Container

Sometimes it's not that you want to style the first or last element in a container, but perhaps you want to style every other element, or every third or fourth element. This is quite common when styling lists and tables: When a list or a table has many items or rows, we often use "zebra styling" by changing the background color of every other item or row.

The :nth-child() pseudo class allows you to select certain elements passed on a pattern, which you place into the brackets/parentheses. There are two special keywords you can use: odd and even. These will select either the odd elements or the even elements (imagine they start numbering at 1, so the first child is odd, the second child is even, etc.)

For example, in this CodePen I have a <section> element containing a collection of <div> elements. I added a CSS rule that styled the odd divs with a background color:

See the Pen Pseudo Classes: Nth-Child (Zebra, no tables) by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the nth-child odd/even demo CodePen, opens in the pen tab)

Try changing "odd" to "even" to see the effect.

You don't have to use "odd" or "even" with :nth-child(). Let's say, for example, you're creating a celebratory web page for a friend or relative's birthday. You are creating a Happy Birthday message in their favourite colours:

See the Pen Pseudo Classes: nth-child Example by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the descendant combinator exercise CodePen, opens in the pen tab)

In the HTML, I've used a level-1 heading with "HAPPY BIRTHDAY" and put each individual letter inside a <span> element: The <span> element is a generic inline element, so this will allow me to apply different styles to each letter.

The first rule in the CSS tab sets the default font colour of all <span>s to purple. The birtday person's favourite colours are purple, orange, and green so you decide to alternate between purple and orange for the letters in "HAPPY BIRTHDAY".

Scroll down in the CSS tab and uncomment the second rule:

span:nth-child(even) {
  color: orange;
}

Here I set the even <span> elements to orange. This is similar to the even/odd div example from earlier. But what if I want to alternate with all three colours? Instead of using "odd" or "even", you can use a pattern:

An+B

In this pattern, the "n" and the "+1" are optional.

The A is an integer number that indicates element number to start your pattern on. Keep the Happy Birthday CodePen open in another tab, and open this new CodePen in a different tab:

See the Pen Pseudo Classes: Nth-Child by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the nth-child demo CodePen, opens in the pen tab)

Try changing the number inside :nth-child(); for example try section > div:nth-child(4) or section > div:nth-child(8). Notice that it styles the nth div that you specified? E.g. if you used :nth-child(4), it styled the 4th div, and if you used :nth-child(8) it styled the 8th div.

The opional "n" in the pattern is for repeating: 3n means start with the third and style every 3rd element. 5n means start with the fifth and style every fifth element. Try changing your value to "3n", e.g. section > div:nth-child(3n). When you do this, it will select and style more than one element: for "3n" it styles the third, sixth, and ninth elements. For "5n" it styles the fifth and tenth elements.

The "+B" in the pattern adjusts the starting point: you can add or or subtact using + or - (minus). For example, "5n-1" means "start on element 4 (5-1 = 4), and style every 5th element from there". So section > div:nth-child(5n-1) will style elements four and nine. That also means that a value like "5n+1" will start on the sixth element (5 + 1 = 6) and style every 5th element from there. If you try section > div:nth-child(5n+1), you might be satisfied to notice that this actually styles elements 1 and 6! That's because normally "5n" would style 5 and 10. But we added 1 so now it styles 6 and 1: it stays consistent, so even though there's no 11th element, it would be consistent to style every 5th element in the set, so it also styles the first element. Play around with different patterns: you don't have to stick with +1 and -1, try other values, also.

There are other, similar selectors you might want to explore on your own:

Exercises

Exercise 1: Now back to our Happy Birthday message. Comment out the nth-child(even) rule. Then scroll down in the CSS tab for an exercise: Add a rule that change every third letter to orange, starting with the first A, and then add a rule that changes every third letter to green, starting with the first P. When you're done, you should see:

H A P P Y B I R T H D A Y

You can find the solution if you scroll down in the CSS tab.

Exercise 2: The instructions are in the CSS tab of the CodePen below.

See the Pen Combinators: Exercise 4 (listless) by Wendi Jollymore (@ProfWendi) on CodePen.

If you get stuck, you can check the solution.

The :root Pseudo Class

The last important pseudo class we will look at in this tutorials is the :root pseudo class. This selects the root element, or the document node, of the DOM tree for your page. You could also use the html selector, but using :root gives you a higher specificity.

The main reason why developers use the :root element is to define CSS Variables. CSS Variables are like custom properties that you can define and store values in, so you can use those values throughout the entire CSS document. For example, if you have a specific colour scheme, it's helpful to define it as a series of CSS variables in the :root, because then changing your theme is simple - just edit the variable values!

For example, I have the following colour scheme defined in the :root of one of my CSS files:

:root {
    --main-colour: #0072b5; /* blue */
    --accent1: #a0daa9; /* green */
    --accent2: #7c568f; /* purple */
    --highlight1: #9bb7d4;  /* light blue */
    --default-text: #333;  /* dark grey */
}

Notice the syntax: each CSS variable starts with -- or 2 dashes. When I ran my web site through the WAVE Accessibility Checker (link opens in the ref tab), it pointed out a contrast issue I had with a specific combination of two of those colours. I use these colours on many, many pages, but I only had to edit that one CSS rule in that one file to fix the problem!

To use one of the variables, just use the var() function and pass it the name of the CSS variable you'd like to use:

aside {
    background-color: var(--highlight1);
}

You can store any valid CSS value in a custom property or variable and use it wherever you would normally place a regular CSS value. We use these most often for colours, but you could do this for other values that many elements have in common, especially if they are for properties that might change when the theme changes. We definitely want to make it easy to change colour themes, but sometimes a theme change also might mean layout, borders, fonts, etc. You can store values that are "likely to change with a theme change" in CSS variables.

Here's an example on CodePen that shows you how I used CSS variables or custom properties to make it easier to change parts of the theme of the site:

See the Pen CSS Variables Example by Wendi Jollymore (@ProfWendi) on CodePen.

(direct link to the root example CodePen, opens in the pen tab)