The Power of the ::before and ::after CSS Pseudo-Elements

What is ::before and ::after Pseudo-Elements and how to use them!

Pseudo-elements are CSS keywords that can be used to style specific parts of an element. Pseudo-elements are declared by the use of double colons (::) after a CSS selector:

seletor::pseudo-element{
   properties: ...
}

Here are some commonly used CSS pseudo-elements:

  • ::first-letter: declare style for the first letter of a text element
  • ::placeholder: declare the style for the placeholder attribute of an input element
  • ::before: create and style a pseudo-element that represents the first child of an element
  • ::after: create and style a pseudo-element that represents the last child of an element

The content of a pseudo-element is defined through its content property:

seletor::before{
   content: "value"
}

Proper use of ::before and ::after can improve code maintainability, avoiding the use of unnecessary tags and contents, as we will see in the examples below.

Practical examples

All you will need to follow through the examples below is a text editor and a web browser of your preference.

Each example is structure around two files: index.html and main.css. Let us assume index.html file has the template below.

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" type="text/css" href="main.css">
  <link rel="stylesheet” type=”text/css"   href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.1/css/bootstrap.min.css">
 </head>
 <body>
   <!--YOUR EXAMPLE CODE HERE-->
 </body>
</html>

This imports the main.css, which is the file where we will put our stylesheet, and bootstrap.min.css (fetched from a CDN,) which will provide some styling out of the box for the examples to follow, thus letting us focus on applying the ::before and ::after pseudo-elements.

Example 1: required input label

It is a common pattern in user interface design to place an asterisk sign right before the label’s content of a required input field. Consider the HTML snippet below, which implements a form with a required e-mail input field and an optional cellphone number field.

<form class="p-5 d-flex flex-column col-3">
    <label class="required" for="email">E-mail</label>
    <input required id="email"/>
    <label for="cellphone">Cellphone</label>
    <input id="cellphone"/>
</form>

The e-mail input field is declared as a required field and its associated label is stylized by the required CSS class, implemented in our main.css file:

.required::before{
    content:  "*";
    color: #f80;
}

Click here to see the expected result on Codepen.

Note how this approach enforces styling and markup segregation: even though the asterisk sign could have been easily placed as part of the content of the label tag in the index.html file, it is, in this context, a stilling mark whose function is to catch the user’s attention to a required input field and, therefore, must be placed in the main.css file.

Example 2: Badges

The ::before and ::after pseudo-elements enable us to create shapes and effects that wouldn’t otherwise be possible by only using CSS classes. In this example, we are going to create a simple "On Beta" badge and use the ::before and ::after pseudo-elements to define its shape. Click here to see the expected result on Codepen.

Consider the content below for the index.html file, where we declare a card containing a div stylized with the my-badge CSS class.

<div class="card p-4 border-dark col-6 m-5">
      <div class="my-badge">BETA</div>
      <h1>Card title</h1>
      <p>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>
      <p>If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarassing hidden in the middle of text</p>
</div>

This time, our main.css file will be a little more complex:

.my-badge {
  position: absolute;
  border-bottom: 30px solid #4d9c40;
  border-left: 30px solid transparent;
  border-right: 30px solid transparent;
  height: 0;
  width: 120px;
  text-align: center;
  transform: rotate(-45deg);
  left: -40px;
  top: 5px;
  color: #fff;
}

.my-badge::before {
  content: "";
  border-bottom: 11px solid #2d6824;
  border-left: 11px solid transparent;
  transform: rotate(45deg);
  left: -27px;
  top: 24px;
  position: absolute;
}
.my-badge::after {
  content: "";
  border-bottom: 11px solid #2d6824;
  border-left: 11px solid transparent;
  transform: rotate(45deg);
  left: 76px;
  top: 24px;
  position: absolute;
}

The my-badge CSS class creates a trapezoid and places it at the top left corner of the card. The ::before and ::after are then applied to create two darker triangles, which are rotated and placed behind the trapezoid, giving the depth effect in the badge’s background.

Without the ::before and ::after pseudo-elements, we would probably need to create two empty div tags and style them:

<div class="my-badge">
     <div class="left-shadow"></div>
     <div class="right-shadow"></div>
     BETA
</div>

This, of course, comes at the cost of breaking styling and markup segregation, since HTML tags are being placed as styling elements.

Example 3: Transitions

The ::before and ::after pseudo-elements can be used in conjunction with CSS3 transitions to add awesome live effects to our applications.

In this last example, we are going to create a button with a hover effect. Click here to see the expected result on Codepen. This time, our HTML is going to be simple, just a button with a button class:

<button type="button" class="button">Hover me</div>

In our stylesheet, we are going to declare the button CSS class and the ::before and ::after pseudo-elements. We are also going to use the CSS pseudo-class :hover to create the animation which is triggered when the user’s mouse passes over the button.

Our main.css should look like this:

button {
  color: white;
  padding: 0.5rem 2rem;
  margin: 3rem;
  position: relative;
  border: none;
  z-index: 0;
}

.button::before {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  opacity: 0;
  transition: all 0.5s ease-in-out;
  border: 1px solid #112e42;
  border-left-width: 0;
  border-right-width: 0;
  transform: scale(0.1, 1);
}

.button::after {
  content: "";
  position: absolute;
  background-color: #4c5f6d;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transition: all 0.3s ease-out;
  z-index: -1;
}

.button:hover {
  background-color: #fff;
  color: #4c5f6d;
}

.button:hover::before {
  opacity: 1;
  transform: scale(1, 1);
}

.button:hover::after {
  transform: scale(0.1, 1);
  opacity: 0;
  color: #4c5f6d;
}

In this example, we applied the ::before to style the top and bottom borders of the button, while the ::after pseudo-element handles the button’s background color. Thus, these pseudo-elements are applied in conjunction with transform and transition properties to provide different animations to different parts of an element.

Conclusion

In this post we had a brief introduction to pseudo-elements and how to use the ::before and ::after to improve the quality of our code and create awesome effects. From now on, every time you find HTML elements being used exclusively with a style purpose, think twice if these elements shouldn’t be replaced by pseudo-elements.

Unfortunately, it is impossible to address all the power of these pseudo-elements in a single post, but I hope that, with the examples above, you may see the infinite possibilities that they allow us to create.

For further reading

https://blog.logrocket.com/a-guide-to-css-pseudo-elements/
https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements

We want to work with you. Check out our "What We Do" section!