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.
Styling Links with Pseudo Classes
A very popular use for these pseudo classes is with the
anchor tag. You can use certain pseudo classes to style links
when the user clicks them, hovers over them, and when they're
already visited.
:link - how a link looks
when it hasn't been visited
:visited - how a link
looks when it has been visited
:hover - how a link looks
when the user hovers over it. You can use this pseudo-class
on many elements, not just links! NOTE that this
pseudo-class overrides :link and :visited while the
user hovers over the link.
:active - how a link
looks while the user is pressing on it with their mouse
(or finger, on a touch screen). Sometimes developers
also like to use this pseudo-class on buttons, too.
NOTE that this pseudo-class overrides :link and :visited
while the user presses the link.
The example below shows you each of the link pseudo classes
and how they work (you might have to copy and paste the HTML
and CSS into a page of your own to get the "active" to work).
Experiment with the example: you can add things like bold/italic, text decorations,
borders, etc. Note that due to the specificity of these pseudo
classes, you must define your rule with :link and :visited first,
then :hover, and then :active.
Exercise
The instructions are in the CSS tab of the CodePen below. Make
sure you read the code in both the HTML and CSS tabs.
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:
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>:
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:
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.
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.
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:
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:
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:
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:
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:
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:
HAPPYBIRTHDAY
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.
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: