CSS: Foundation — Pseudo-Classes

CSS Pseudo-Classes

Learning outcomes:

  • What are pseudo-classes
  • A simple example
  • Commonly used pseudo-classes in CSS
  • Interaction-based pseudo-classes — :hover, :active, and :focus
  • Structural pseudo-classes — :nth-child(), :nth-of-type(), etc.
  • Other useful pseudo-classes — :checked, :disabled, :target, and :not()

In the last chapter, CSS Selectors, we learned a great deal about selectors in CSS; how they're used in styling given elements; the different kinds of selectors in CSS; and so on and so forth.

There, we briefly learned about a classification of simple selectors — pseudo-classes. This is what we shall explore in detail in this chapter.

In particular, we'll learn what exactly are pseudo-classes; why are they needed in addition to other simple selectors in CSS; some commonly used pseudo-classes in CSS; and much more.

What are pseudo classes?

Let's start with the definition:

A pseudo-class is a CSS selector that represents a special characteristic of an element.

A pseudo-class represents a characteristic of an element that is otherwise not representable by attributes on the element or least not as simply representable.

Recall that attributes in HTML are also used to describe certain characteristics of an element

For example, consider an element that currently has the mouse pointer over it — this element has the characteristic of being 'hovered over.' Similarly, consider an input field that is disabled. This is another characteristic of the element.

In CSS, a pseudo-class is denoted using a : (colon). The general syntax of a pseudo-class is :name, where name is the name of the pseudo-class.

Recall from the last chapter that a simple selector is a selector in CSS that can't be divided any further. An example is p, a type selector selecting all <p> elements in the document. A pseudo-class is also an instance of a simple selector.

Why are pseudo-classes are called 'pseudo'?

The word 'pseudo' has a straightforward meaning; it means 'fake', 'not genuine'.

In the concept of pseudo-classes, it signifies the fact that these are not 'real' classes of elements but rather created by the browser itself for specifically targeting certain aspects of elements and styling them.

It's now time for an example before we explore different kinds of pseudo-classes in CSS.

A simple example

Consider the following button:

HTML
<buton>A button</buton>
CSS
button {
   background-color: white;
   color: black;
   padding: 10px;
   font-size: 18px;
   border: 1px solid black;
}

It just has a few basic styles on it in order to make it visually more pronounced:

Now suppose we want to change the background color of this button to black and the text color to white right when we bring the mouse pointer over it. This helps in immediately noticing that the button is interactive (i.e. could be clicked on).

How to accomplish this task?

Well, this is a special characteristic of the element — that is, when it is being hovered upon using the mouse pointer — which can't otherwise be handled by any HTML element, or any of its attributes. What we seek here is a pseudo-class.

Speaking of which, the :hover pseudo-class represents an element in its hover state, i.e. when the mouse pointer is over the element.

:hover is superbly common. You'll see it in every other webpage, probably because of its ubiquity.

Desktop-based web surfers make up quite a large percentage of all web surfers, and it makes sense to add hover interactivity to elements on webpages because desktop devices work alongside mice (not the mice that love cheese!).

Let's now accomplish the task above using :hover.

Consider the following CSS with :hover set up:

CSS
button:hover {
   background-color: black;
   color: white;
}

Before anything, hover over the button (i.e. bring the mouse pointer over it) below to see the effect of the pseudo-class in action:

Notice the background color changing? Behold, this is only the beginning of pseudo-classes; there's a lot we ought to cover in this chapter.

Coming back to the topic, in the code above, we've paired the :hover pseudo-class with the button type selector. This, in effect, means that we're selecting all buttons in their hover state.

:hover isn't limited to just buttons; we can use it on any element in HTML (although it's more common to have it set up for interactive elements, like <button>).

In the following code, we've set up the :hover pseudo-class for a <p>:

HTML
<p>This is a paragraph</p>
CSS
p {
   background-color: pink;
   padding: 10px;
}

p:hover {
   background-color: yellow;
}

As before, hover over the paragraph below in order to witness the magic of the :hover pseudo-class:

This is a paragraph

As you do so, you'll of course notice the background color changing to yellow. This is the power of :hover, and most importantly of pseudo-classes.

With this example out of the way, it's now time to explore the huge world of pseudo-classes in CSS, followed by going over some more commonly used ones in the following sections.

List of pseudo classes

There are tons and tons of pseudo-classes in CSS at the time of this writing. And it's highly likely that more will cripple into CSS in the coming days, months, or years.

The discussion below enumerates some of the most common and useful pseudo-classes along with their purpose.

Stating all pseudo-classes is out of the scope of this chapter. But if you wish to explore them all, you're encouraged to refer to Selectors Level 4 — W3C.

Because of the large number of selectors that are pseudo-classes, it's helpful to group them under different categories to make the comprehension a little bit easier and intuitive.

Here are all pseudo-classes that are tied to interacting with elements on a webpage. We've already seen a typical example of one of these above, that is, :hover.

Pseudo-classMeaning
:hoverThe subject (i.e. the element selected by the entire selector, ending at :hover) is in the hover state, i.e. the mouse pointer is over it.
:activeThe subject is about to receive focus. That is, when the mouse button is pressed (but not yet released) while the pointer is over the subject.
:focusThe subject is in focus; henceforth, it can accept keyboard events on it.
:focus-visibleSame as :focus in addition to the fact that the subject's focus needs to be clearly made visible to the user.
:focus-withinOne of the descendants of the subject is in focus.

Apart from this, some pseudo-classes are tied to the structure of the underlying HTML document, i.e. which elements are inside of given elements, which elements have nothing inside of them, and so on. These are sometimes referred to as structural pseudo-classes.

The table below illustrates structural pseudo-classes:

Pseudo-classMeaning
:rootThe subject is the document's root element. For HTML document, it's <html>.
:emptyThe subject is empty, i.e. doesn't have any content in it.
:nth-child(an + b)The subject is the (an + b)th child of its parent. (We'll learn more about this in detail below.)
:nth-last-child(an + b)The subject is the (an + b)th last child of its parent. (We'll learn more about this in detail below.)
:first-childThe subject is the first child of its parent. Same as :nth-child(1).
:last-childThe subject is the last child of its parent. Same as :nth-last-child(1).
:only-childThe subject is the only child of its parent. Same as :first-child:last-child.
:nth-of-type(an + b)The subject is the (an + b)th child of its parent of its type. (We'll learn more about this in detail below.)
:nth-last-of-type(an + b)The subject is the (an + b)th last child of its parent of its type.
:first-of-typeThe subject is the first child of its parent of its type. Same as :nth-of-type(1).
:last-of-typeThe subject is the last child of its parent of its type. Same as :nth-last-of-type(1).
:only-of-typeThe subject is the only child of its parent of its type. Same as :first-of-type:last-of-type.

Besides structural pseudo-classes, some of these selectors are targeted towards form-related elements, which we can also refer to as input pseudo-classes.

Pseudo-classMeaning
:requiredThe subject (an input element in this case) is required.
:read-onlyThe subject is read-only.
:checkedThe subject (radio or checkbox) is checked.
:disabledThe subject is disabled.
:validThe subject contains valid data.
:invalidThe subject contains invalid data.
:placeholder-shownThe subject has its placeholder shown (because it's empty).

Not only this but we also have some logical pseudo-classes that further filter the subjects of a given selector:

Pseudo-classMeaning
:is()The subject matches the given selector list.
:not()The subject does not match the given selector list.
:where()The subject matches the given selector list. Same as :is() except for that :where() doesn't increase the specificity of the selector. (We'll learn more about specificity later in this unit, in CSS Style Precedence.)
:has()The subject has given elements inside of it, as identified by the given relative selector list.

Apart from this, there are some location-based pseudo-classes:

Pseudo-classMeaning
:linkThe subject is a hyperlink that hasn't been visited yet.
:local-linkThe subject is a hyperlink whose URI is the same as the current document's URI.
:visitedThe subject is a hyperlink that has been visited.
:targetThe subject is the document's target, i.e. the element whose ID is the same as the document's fragment as pointed to by its URI.

Let's now start exploring some of the more common of these pseudo-classes in the sections below.

Interaction-based pseudo classes

:hover

Even though we've explored :hover in the preceding discussion, let's have a look at it again. It's so common in webpages that it wouldn't hurt witnessing it once again. What do you say?

The :hover pseudo-class represents an element in its hover state, that is, when the element has the mouse pointer over it.

Dynamic pseudo-classes

Some pseudo-classes in CSS are dynamic. That is, they can apply or no longer apply to given elements as we interact with them. :hover is an example of a dynamic pseudo-class.

Dynamic pseudo-classes basically arise from characteristics of elements that are dynamic in themselves.

For example, reconsider the 'hovered over' characteristic. Obviously this only applies so long as the mouse pointer is over the element in question. Likewise, the moment the characteristic disappears, i.e. the mouse pointer goes outside the element, so does the corresponding :hover pseudo-class.

Here's a practical example for :hover:

HTML
<button>This is a button</button>
<button>This is another button</button>
CSS
button:hover {
   background-color: blueviolet;
   color: white;
}

Live Example

It's worthwhile noting here that although :hover can be set up on any HTML element, it's more sensible to only do so on interactive elements, or at least mostly on them.

This is because it's only mostly on interactive elements that we need to provide visual feedback (that those elements are interactive).

It doesn't really make much sense to set up :hover on non-interactive elements like <p>, <h1>, etc. as the user is most likely to not assume them as interactive (and rightly shouldn't).

:active

The :active pseudo-class represents the state whereby an element is about to be brought into focus.

More specifically, when we press the mouse button, but not release it, while the pointer is over an element, that element is matched by :active.

Let's bring in the same example as above for :hover, just this time replacing :hover with :active:

CSS
button:active {
   background-color: blueviolet;
   color: white;
}

Open up the example below and try hovering over a <button>, expecting nothing to happen. Then, click on the <button> but make sure to not let go off the mouse button as you do so. Then finally release the mouse button.

Live Example

What did you notice throughout this series of interactions?

Well, as soon as the mouse button is clicked, the <button> becomes active, and thus the :active pseudo-class applies. But as soon as the mouse button is released, the pseudo-class ceases to apply.

Quite simple.

The :active pseudo-class is handy when we want to style buttons (or other interactive elements) differently when they are depressed (pushed down).

As an example, consider the following:

Live Example

Clearly, we haven't covered much of the stylistic properties being used in this example but the idea is evident — as we depress the button, we get a familiar depressed style.

The :active pseudo-class also applies when we press the spacebar key while an interactive element has focus. Precisely speaking, so long as the spacebar key stays depressed, the pseudo-class applies; the moment we release the key, the pseudo-class ceases to apply (similar to releasing the mouse button).

:focus

The :focus pseudo-class applies to elements when they receive focus.

Speaking of which, an element receives focus when it is clicked upon or when it is tabbed to (i.e. navigated to using the Tab key) and if the element is focusable.

Interactive elements in HTML are focusable by default. However, others are not. For example, <p> is not focusable by default and therefore can't take up the :focus pseudo-class.

Keep in mind that a focusable element receives focus as soon as the mouse button goes down while the pointer is over it; the click action doesn't have to be a complete click, so to speak.

In this way, :focus is similar to :active except for that :focus remains as it is even after the mouse button is released unlike :active which, as we know, goes away.

As before, let's reconsider the same <button>s example, this time obviously replacing :hover with :focus:

CSS
button:focus {
   background-color: blueviolet;
   color: white;
}

Live Example

The :focus pseudo-class is handy when we want to specifically style elements when they receive focus. This is more important than you might think right now. But how?

Well, the reason is because not everyone uses the mouse to navigate around a webpage; sometimes, the keyboard is used instead which means that the user must be clearly presented with visual cues as to which element on the webpage is currently under focus.

Thus, it won't be wrong to say that pseudo-classes help improve the accessibility of a webpage by providing visual cues as users interact with elements.

:focus-visible and :focus-within

The two pseudo-classes, :focus-visible and :focus-within, are quite similar to :focus but certainly not identical. They differ in subtle, yet important ways.

If you'd like to learn more about these two selectors, you're encouraged to read the article What is the Difference Between :focus, :focus-visible, and :focus-within in CSS?.

Structural pseudo-classes

Structural pseudo-classes have to do everything with the structure of the HTML document. Let's see some of the most commonly used pseudo-classes in this category.

:nth-child()

The :nth-child() pseudo-class applies to any element that is literally the nth child of its parent.

For example, :nth-child(1) applies to all elements that are the first child elements of their parent. Similarly, :nth-child(10) applies to all elements that are the tenth child elements of their parent.

Consider the following HTML code:

HTML
<div>
   <p>The first paragraph</p>
   <p>The second paragraph</p>
</div>

Here, the <div> contains two <p> children. p:nth-child(1) would only, however, apply to the first <p>.

The CSS below confirms this:

CSS
p:nth-child(1) {
   background-color: yellow;
}

Live Example

:nth-child(0) doesn't select anything.
Here the term 'child' specifically refers to 'element child'. This distinction is important to be aware of because in HTML, mere pieces of text inside an element are also children of that element. However, CSS doesn't treat mere pieces of text as children in :nth-child() and all other structural selectors.

The real potential of :nth-child() lies in the fact that it could be provided an expression to refer to multiple children in one go. This expression, akin to the expression for computing the nth term of an arithmetic sequence, is of the general form:

an + b

Here, a and b are both provided by us; n on the otherhand is itself plugged into the expression by CSS itself in order to get to concrete number. The value of n begins at 0 and increments by 1.

So, 3n + 1 gives us 1, 4, 7, 10, and so on. :nth-child(3n + 1) is equivalent to manually setting up each of :nth-child(1), :nth-child(4), :nth-child(7), and so on.

Let's take an example.

Which positions does the pseudo-class :nth-child(3n) target?

  • 1, 2, 3, 4, ...
  • 3, 6, 9, 12, ...
  • 4, 7, 10, 13, ...

The expression 3n means the positions 3 (when n = 1), 6 (when n = 2), 9, 12, 15, and so on.

Suppose we want to select all even-positioned <tr> elements (inside a <tbody> element) in a table. This is a real, practical scenario, used to give a stripe style to a table, i.e. even and odd rows with differing backgrounds.

Anyways, coming back to the task, to select all even positioned elements, we need to target elements in the 2nd, 4th, 6th, 8th, ..., positions. This means that the expression would be 2n (remember that n begins at 0).

The following CSS code demonstrates such an example for a table along with some other styling as well:

CSS
th {
   background-color: gray;
   color: white;
}

tr:nth-child(2n) {
   background-color: lightgray;
}

Live Example

Notice how the 2nd and 4th rows, following the table's header, have the lightgray background, courtesy of the :nth-child(2n) selector.

The special keywords even and odd

Since 2n and 2n + 1 are both commonly used expressions with regards to :nth-child(), CSS has handy shortcuts for them: even and odd, respectively.

Hence, :nth-child(2n) is the same as :nth-child(even). Similarly, :nth-child(2n + 1) is the same as :nth-child(odd).

:nth-last-child()

The :nth-last-child() selector is identical to :nth-child() except for that it, as you can guess, starts from the end of a given element in enumerating its children.

So where :nth-child(1) selects the first element child of every element, :nth-last-child(1) selects the last element child (which is the first element child from the end) of every element.

And just like, :nth-child(), :nth-last-child() also supports an expression of the form an + b (with the exact same behavior but obviously from the end of the parent element).

:first-child and :last-child

Because :nth-child(1) and :nth-last-child(1) are common pseudo-classes, CSS provides us with concise selectors to accomplish similar effects: :first-child and :last-child, respectively.

:nth-of-type()

Where :nth-child() selects children elements in given positions, :nth-of-type() accomplishes the same thing albeit it only focuses on the children of a given type (i.e. tag name).

For example, as we know, :nth-child(2) would target the second element children of their parents. In contrast, :nth-of-type(2) would target second element children of their parents from the list of all children of the given type.

If this is unclear, the following example will hopefully clarify the confusion. (Well, honestly speaking, an example is really needed over here.)

Consider the following <div>:

HTML
<div>
   <p>The first paragraph</p>
   <blockquote>The first blockquote</blockquote>
   <p>The second paragraph</p>
</div>

First, take the list of all children elements of this <div>. We have a <p>, followed by a <blockquote>, followed by another <p>.

What is the first element in this list?<p>, right? This is precisely what gets selected by p:nth-child(1).

Now, take the list of all children elements of this <div> that are of type <blockquote>. We have <blockquote>, and that's it. Alright.

What is the first element in this list?<blockquote>, right? And there you have it. This is precisely what gets selected by blockquote:nth-of-type(1).

The <blockquote> element would NOT be matched by the blockquote:nth-child(1) selector because it isn't the first child of its parent. However, it would certainly be matched by blockquote:nth-of-type(1) because it indeed is the first <blockquote> element in its parent; in other words, there is no <blockquote> before it.

Notice the subtle but important distinction between :nth-child(1) and :nth-of-type(1). The former targets the first child element from the list of all child elements. In comparison, the latter targets the first child element from the list of all <blockquote> child elements.

Here's some CSS code to style the first <blockquote>:

CSS
blockquote:nth-of-type(1) {
   background-color: yellow;
}

Live Example

Had the selector here instead been blockquote:nth-child(1), nothing would have been selected because the <blockquote> element in the code above isn't the first child element of its parent; it's only the first child <blockquote> element.

Here's a quick quiz to solve.

For the same HTML code above, what would p:nth-of-type(2) select?

  • Nothing
  • <p>First paragraph</p>
  • <p>Second paragraph</p>

It would select the second paragraph because it's indeed the second <p> child element of its parent (but obviously isn't the second child element; that is <blockquote>).

Moving on, akin to :nth-child() and :nth-last-child(), :nth-of-type() also supports an expression of the form an + b.

:nth-last-of-type(), :first-of-type, and :last-of-type

Similar to how we have corresponding classes for :nth-child(), so do we for :nth-of-type().

They are :nth-last-of-type(), :first-of-type, and :last-of-type. Once you understand :nth-child(), :nth-last-child(), and :nth-of-type(), there's a 100% probability that you'd be able to understand these three selectors.

For brevity, I'll be skipping them but you're encouraged to play and experiment around with them on your end. Experimentation is healthy!

Other useful pseudo-classes

:checked

The :checked pseudo-class is amongst the category of pseudo-classes that apply to input elements (<input>s, <button>s, <textarea>s, etc.).

Speaking more about it, :checked applies when an input element — more precisely, a radio or a checkbox — is checked.

Consider the following example:

HTML
<label>
   <input type="checkbox" name="terms">
   <span>I agree to the terms and conditions</span>
</label>

We have a simple checkbox input inside of a <label>, part of a larger (hypothetical) form where the user creates a new account on an online service.

Let's style this checkbox differently when it's checked.

Now because a checkbox has a special default appearance on browsers, which can't otherwise be modified by properties such as color, background-color, or border, we need some property that we could easily apply to it.

This property can be opacity. We'll learn more about opacity in CSS Colors — Opacity but the simple explanation of it is that it controls the opacity (how much is visible) of a given element.

To be more specific, opacity takes a number between 0 and 1, both inclusive, which tells about the opacity of the element. 0 means nothing is visible (although the element is right there in the document) whereas 1 means complete visibility (the default).

In our example, let's set the opacity of the checkbox to 0.3 to make it near invisible when it's checked, obviously leveraging the :checked pseudo-class:

CSS
input:checked {
   opacity: 0.3;
}

Live Example

As you click on the checkbox, notice its appearance — the opacity: 0.3 style would reduce it considerably.

:checked is quite useful a pseudo-class in CSS. It can be used to create customized radio buttons and checkboxes in HTML.

Take a look at the example below to see what's possible using the :checked pseudo-class, as an opportunity to convince you further about the breadth of knowledge you'll gain in this course:

Live Example

The specifics of this advanced styling require us to know about some other CSS properties which we will eventually as this course progresses further.

But what we can take away right now from the example is that: CSS is exceptionally powerful!

:disabled

The :disabled pseudo-class applies when an input element is disabled.

In other words, when an input element has the disabled attribute set on it (and contains the value "true" or just exists as a Boolean attribute), the input element matches :disabled.

Shown below is an example using a <button>:

HTML
<button>A normal button</button>
<button disabled>A disabled button</button>

Here, we have two buttons of which one is disabled. The following CSS styles this disabled button specially:

CSS
button {
   background-color: blue;
   color: white;
   border: 2px solid navy;
   font-size: 18px;
}

button:disabled {
   opacity: 0.3;
}

Because disabled form fields aren't able to be interacted with, it's a wise decison to reduce their opacity considerably down. The CSS rule above for the pseudo-class :disabled does exactly this.

Live Example

Browsers, by default, style disabled form fields differently. In particular, they change their background color and text color to produce a 'grayed-out' effect.

Go on and see this default style by creating a <button> in a blank HTML document and then disabling it by setting the disabled attribute.

:target

The :target pseudo-class applies to the target of the underlying HTML document. So what exactly is the target?

Well, the target is the element referred to by the URI of the document. But how can a URI refer to a particular element? Hmm... You already know this. Let me make this simpler for you. You already know this from the HTML URIs chapter.

When the URI of the HTML document contains a fragment in it, it refers to a particular element in the document — an element whose ID matches the fragment identifier in the URI.

So, for example, suppose that we have the URI https://example.com/#p1, with the fragment as #p1, and the following <p> element:

HTML
<p id="p1">A paragraph</p>

Visiting this URI would, likewise, have the underlying document's target as the shown <p> element because its ID matches the fragment identifier.

Now, often, it's desirable to uniquely style the target of a document so that the user clearly knows which specific element the given URI points to. This is something that can easily be done with the help of the :target pseudo-class.

An example follows.

In the HTML below, we have a hyperlink to go to a fragment in the same document. Also, we have a <div> with id="d1" set, which is what the hyperlink points to:

HTML
<p><a href="#d1">Go to #d1</a></p>

<div id="d1">This is the #d1 div</div>

It's all pretty basic of a setup. Now, let's apply a yellow background to the target element in case the document's URI contains a fragment:

CSS
:target {
   background-color: yellow;
}

Open up the linked document and click on the hyperlink:

Live Example

As you do so, notice the background color of the <div> changing to yellow because of it becoming the target of the document.

:not()

The :not() selector is another instance of a functional selector in that it accepts arguments.

As the name suggests, :not() applies to elements that do NOT match a given list of selectors; this list is provided within the pair of parentheses (()).

For example, suppose we want to match all <p> elements that don't have the class wrong on them. This can be accomplished using the compound selector p:not(.wrong). Here, .wrong is provided to :not() because it's the selector that should not be matched by the selected <p> elements.

Spread the word

Think that the content was awesome? Share it with your friends!

Join the community

Can't understand something related to the content? Get help from the community.

Open Discord

 Go to home Explore more courses