Pseudo-Elements and Pseudo-Classes in CSS

Published in WordPress.

Watch our video

There is a better version of your web

Share this post

Every time we have to modify a CSS style on our web, Toni and I play rock-paper-scissors to see who is the “lucky guy” who gets to do the task. How come, you may ask? Well, for a long time working with HTML and CSS has been a pain in the a**, as fixing something on one end broke a lot of things elsewhere.

One cannot help but admire and marvel at the work done by front-end developers.

But the truth is CSS has changed a lot in recent years and if you take advantage of its new functionalities, it’s much easier to layout and style your website properly and effectively. In my opinion, two of the great advances in CSS we’ve recently seen are the inclusion, first, of flex (to layout rows and columns) and, a little later, grid (to layout grids).

CSS is full of features that can simplify your work. And today we are going to see two of them: pseudo-elements and pseudo-selectors.

How to “Create Content” with CSS Pseudo-Elements

CSS pseudo-elements are a mechanism to access parts of the HTML that do not have an associated node in the DOM. For example, a “part of our document” might be “the first line of a paragraph,” “the first letter of a paragraph,” or “the selection made by the user,” none of which has an associated node. But pseudo-elements are also a way to create content before or after an existing element through CSS.

The pseudo-elements available in CSS3 are:

  • ::before: adds content before an element
  • ::after: adds content after an element
  • ::first-letter: selects the first letter of “block” element (that is, those whose display property is set to block, inline-block, table-cell, etc.)
  • ::first-line: selects the first line of a “block” element
  • ::selection: refers to the parts of the document that the visitor has selected

As you can see, pseudo-elements are characterized by starting with a pair of colons (::). And this is how you use them.

Selecting Parts of the Document

Suppose, for example, that we want a certain paragraph to have the first line in orange and underlined. Well, just do something like this:

p::first-line {
  color: orange;
  text-decoration: underline;
}

and you’ll get the correct result:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mollis leo in turpis interdum, at pharetra velit elementum. Nam eget lacus sed lacus dictum scelerisque. Proin porta ligula sed mi semper, vel interdum massa auctor. Pellentesque et massa congue, tincidunt erat ac, tincidunt elit. Sed ac dolor metus. Nam commodo metus sed ligula consectetur interdum. Curabitur odio est, tempor id egestas nec, aliquam at nisl.

If, on the other hand, you want to modify the selection, the approach is exactly the same. Just keep in mind the number of CSS properties we can tweak is limited to color, background, cursor and outline:

p::selection {
  background: #21acde;
  color: white;
}

You can see the result in the following paragraph, assuming you select something:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mollis leo in turpis interdum, at pharetra velit elementum. Nam eget lacus sed lacus dictum scelerisque. Proin porta ligula sed mi semper, vel interdum massa auctor. Pellentesque et massa congue, tincidunt erat ac, tincidunt elit. Sed ac dolor metus. Nam commodo metus sed ligula consectetur interdum. Curabitur odio est, tempor id egestas nec, aliquam at nisl.

Creating New Content

You also have the option to dynamically add content to your page. For example, imagine you have a list of items like this:

<ul>
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

Usually, this is how lists look like:

  • One
  • Two
  • Three

but you can tweak it so that it each element is separated from the next one using a blue bar:

  • One
  • Two
  • Three

using the ::after pseudo-element on each item in the list as follows:

ul li {
  display: inline;
}

  ul li::after {
    color: #21acde;
    content: " | ";
    display: inline-block;
  }

With ::before and ::after pseudo-elements you can do many great things . For instance, you can combine multiple backgrounds in a single element, display the URLs of the links when the page is printed, order the floating elements, label blocks, beautify your typography, and so on.

How to Apply Styles More Precisely Using Pseudo-Classes

CSS pseudo-classes allow us to select elements of our website that either are in a specific state or have some special property that sets them appart. The most common (and oldest) example is probably link pseudo-classes:

  • a:link are those links you haven’t visited already
  • a:visited is any link you’ve already visited
  • a:hover is the link you’re hovering
  • a:focus matches the link that currently is focused (which is similar to being hovered, but using the keyboard instead of the mouse)
  • a:active matches the link you’re currently clicking on

In CSS there are about 60 pseudo-classes. A vast majority of them allow you to filter items based on their state (for example, you can select read-only elements using, you guessed it, the :read-only pseudo-class). But there are some interesting “exceptions” to this rule that are worth mentioning.

But before we do, just a quick side-note: notice how you can distinguish a pseudo-class from a pseudo-element because the former starts with a single colon (:) whereas the latter starts with two (::).

Selecting an Element’s Children Using “child” and “of-type” Pseudo-Classes

Remember the example we saw earlier where we added a vertical bar to separate the elements of a list? Well, if you look closely, you’ll see that the rule we set doesn’t quite work:

  • One
  • Two
  • Three

because the last element also includes a vertical bar, even though it shouldn’t. If we want to remove it, we can do something like this:

ul li {
  display: inline;
}

ul li::after {
  color: #21acde;
  content: " | ";
  display: inline-block;
}

ul li:last-child::after {
  display: none;
}

which works exactly as you’d imagine:

  • One
  • Two
  • Three

As you can see, all we are doing is adding a new rule using the pseudo-class :last-child, which allows us to select the last li element in our ul list and, once we have it, hide its pseudo-element ::after with the display property set to none.

Special Features of * -child and * -of-type

There are three variations of the *-child pseudo-class:

  • :first-child matches the first child of an element (duh!)
  • :last-child does the same, but with the latter
  • :nth-child(i), you guessed it, selects the ith element (so, for example, :nth-child(2) matches the second child)

So let’s look at and think about the following CSS:

li strong:first-child {
  color: orange;
  text-decoration: underline;
}

and the following HTML snippet:

<ol>
  <li>Lorem <strong>ipsum</strong> dolor <strong>sit</strong> amet.</li>
  <li><em>Lorem</em> <strong>ipsum</strong> dolor <strong>sit</strong> amet.</li>
  <li>Lorem <strong>ipsum</strong> dolor <em><strong>sit</strong></em> amet.</li>
</ol>

What do you think will happen? In principle, it seems that in all three cases, the word ipsum will be orange and underline, because ipsum is the first strong child in each li element, right? Let’s see:

  1. Lorem ipsum dolor sit amet.
  2. Lorem ipsum dolor sit amet.
  3. Lorem ipsum dolor sit amet.

Well, close enough. Can you guess why did this happen?

The :first-child pseudo-class applies to the first child of an element. In the first sentence, ipsum is the first strong tag, which happens to be the first child to. In the second sentence, the first child node is em (with the word Lorem) and then comes a strong tag with the word ipsum. Therefore, this strong tag is not the “first child.” Finally, in the third sentence there are two tags that matched our rule: ipsum (it’s like the first sentence) and sit. Notice how sit is a strong tag that’s the first child of its parent tag (em).

So, what if we wanted to underline the first strong tag of each li item? Well, we have to use a different pseudo-class and make sure we only match elements that are direct children of li:

li > strong:first-of-type {
  color: orange;
  text-decoration: underline;
}

which works as expected:

  1. Lorem ipsum dolor sit amet.
  2. Lorem ipsum dolor sit amet.
  3. Lorem ipsum dolor sit amet.

Pretty awesome, huh? Again, there are three variations of this pseudo-class:

  • x:first-of-type matches the first sibling whose type is x
  • x:last-of-type selects the last
  • x:nth-of-type(i) matches the ith sibling whose type is x

Selecting the Opposite with :not

Finally, the pseudo-class I wanted to talk to you about today is :not. :not is a pseudo-class that matches those elements that do not meet a certain property. For example, let’s go back to our previous example:

  • One
  • Two
  • Three

To solve the problem of the bar that appears in the last element, we basically (1) added the bar to all the li elements and (2) overwrite the rule in the last element and hide it:

ul li {
  display: inline;
}

ul li::after {
  color: #21acde;
  content: " | ";
  display: inline-block;
}

ul li:last-child::after {
  display: none;
}

But this doesn’t seem very optimal, does it? We’re overshooting with the first tule and we then need to fix our styles in the last element. What we really wanted was to add the bar to all the elements except the last one…

Well, we can achieve this by applying the pseudo-class :not and combining it with :last-child :

ul li {
  display: inline;
}

ul li:not(:last-child)::after {
  color: #21acde;
  content: " | ";
  display: inline-block;
}

which applies the style to all the li elements that are not the last element (:not(:last-child)), which perfectly describes our goal and thus solves our problem:

  • One
  • Two
  • Three

In Summary

CSS pseudo-elements and pseudo-classes allow us to be much precise when accessing the different elements of our website. With them, we can write more concise and specific CSS rules, which simplifies its maintenance and avoids the possibilities of having to continually overwrite generic rules for specific cases.

I hope you liked this post and, if you did, please share it with your friends!

Featured image by JFL on Unsplash.

Leave a Reply

Your email address will not be published. Required fields are marked: •

I have read and agree to the Nelio Software Privacy Policy

Your personal data will be located on SiteGround and will be treated by Nelio Software with the sole purpose of publishing this comment here. The legitimation is carried out through your express consent. Contact us to access, rectify, limit, or delete your data.