Photo by Jackson Sophat on Unsplash

CSS: Understanding the Important Basics. (Pseudo-Elements)

Codesader
7 min readFeb 25, 2023

--

Prerequisite

  • A basic understanding of CSS is required to grasp this topic as this is considered an intermediate level CSS.
  • Knowledge of advanced positioning such as absolute and relative positions.
  • Knowledge of CSS selectors

This article will be divided into two parts. The separation is needed for clarity. The first part will cover pseudo-elements, while the second part will focus on pseudo-classes.

In this first part, we will discuss

  • What pseudo-element is
  • Why we need pseudo-element
  • Common pseudo-elements,
  • How to use these pseudo-elements with detailed examples, particularly with respect to (::before and ::after) pseudo-elements, in order to reinforce our knowledge.

Without further talks, let’s get into it.

What is pseudo-element?

Pseudo-elements, as the name suggests, simply means unreal elements. What do we mean by this? Plain and simple, pseudo-elements are imaginary or pretend elements which do not actually exist in our markup (HTML) document. For example, elements such as div, h1, section, p do really exist in our HTML document. In the case of pseudo-elements, there are no actual tags for them. However, they can mimic the properties of an actual element e.g they can have a background, width, height, colour, padding etc.

You may then ask, “what exactly is the purpose of pseudo-element if we already have actual elements.” Great question. The primary purpose of pseudo-element is to style other elements without having to introduce needless tags in our HTML document which may lead to

  • bloating of HTML document i.e cumbersome file,
  • confusion to other developers that may wind up maintaining the code,
  • unnecessary tags

Imagine having individual tag (element) to do the simplest of styling. For example, say, a tag (element) to style just the first letter, or first line or first word of an element etc.? This is a treacherous path with no ending in sight.

Luckily for us, CSS, once again, comes to the rescue by giving us pseudo-elements to handle these unusual cases.

Having discussed the purpose of pseudo-elements above, I am sure you can now understand why there is a need for them, and hopefully will harness their full potential in your projects.

Some of the pseudo-elements we shall discuss include:

  1. ::before
  2. ::after
  3. ::first-line,
  4. ::first-letter
  5. ::marker
  6. ::placeholder
  7. ::file-selector-button

1. ::before and ::after

Pseudo-elements ::before and ::after are the most commonly used pseudo-elements in CSS. These are the ones you would most likely come across in a stylesheet. They are used to insert some form of content either before, or after the content of an element. Hence the name ::before and ::after.

The pseudo-elements (::before and ::after) give us more freedom to do some tricky styling that would otherwise prove difficult to achieve through traditional methods.

The syntax is straightforward. To create a pseudo-element, a selector is immediately suffixed with double column (single column also works especially for backward compatibility) and the pseudo-element like so selector::pseudo-element e.g h1::before, .banner::after etc.

Giving a simple example is perhaps better than going into a lengthy explanation that may do little to no justice.

Let us quickly consider this simple example below:

<div>
<h1>a short tiltle</h1>
</div>
h1::before {
content: "\"";
color: red;
}

h1::after {
content: "\"";
color: red;
}

In the example above, we put a double quotation mark before and after the text inside the h1 element using pseudo-element. This may be achieved with ordinary HTML entity like (&#34), but I wanted to demonstrate the simplest way to use ::before and ::after, and to also get you acquainted with the syntax, before we delve into a relatively complex use case.

Note: the content property of the pseudo-element is a mandatory property. It must be present for any other properties to have effect. However, the value may be empty. e.g content: “”;

Consider a somewhat complex use case of pseudo-elements ::before and ::after below

<figure>
<img src='https://source.unsplash.com/random/1920x1080/?wallpaper,landscape'>
</figure>
* {
margin: 0;
padding: 0;
} /* to get rid of the default margins and paddings that may come with all elements */

figure {
position: relative;
width: 500px;
height: 300px;
background-color: skyblue;
margin: 0 auto; /* to center the box */
}

img {
display: block;
width: 100%;
height: inherit;
max-width: 100%;
}

figure::before {
content: "";
background-image: linear-gradient(to right, rgba(255, 0, 0, .5), rgba(255, 0, 0,.4));
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 1;
transition: opacity 500ms ease-out;
}

figure:hover::before {
opacity: 0;
transition: opacity 500ms linear;
}

The example above creates a linear-gradient-colour overlay on top of the actual image. By giving the <figure></figure> element a position of relative, the pseudo-element (::before) with position: absolute knows to start its position (top, right, left, bottom) in relation to the <figure></figure> element. i.e within the boundaries (width and height) of the figure element, start from top: 0, left: 0, right: 0 and bottom: 0. The gradient background will overlay the entire <figure></figure> element as a result.

The hover effect is added to see the original image before the gradient overlay.

The example below illustrates how we can add effect to a button element using pseudo-element ::before

<button>submit</button>
button {
padding: 0.5rem 2rem; /* vertical(top and bottom) horizontal(left and right) */
text-align: center;
font-family: "segoe ui", sans-serif;
background-color: pink;
width: max-content; /* this is to force our div to assume the width of its content and not its parent container */
position: relative;
border: none;
cursor: pointer;
}

button::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
width: 0;
height: 100%;
background-color: rgb(46, 79, 164, .5);
transition: width 200ms linear;
}

button:hover::before {
transition: width 300ms linear;
opacity: .5;
width: 100%;
}

2. ::first-line

The pseudo-element ::first-line can be used to style the first line of a block-level element to make it distinct.

The syntax is selector::first-line e.g p::first-line, .banner::first-line (in the case of a class selector) etc.

<div>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit, cumque!</p>
</div>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

p {
width: 30ch; /* I gave the paragraph a small width in order to divide the paragraph into several lines. "ch" is another unit in css */
padding: 2rem;
margin: 0 auto;
background-color: rgba(0, 0, 0, .8);
color: #eaeaef;
line-height: 1.6;
}

p::first-line {
color: red;
text-transform: capitalize;
font-size: 1.2rem;
font-weight: bold;
font-style: italic;
}

Note: Not all CSS properties work on ::first-line. Check out the list of all permitted properties on this MDN link.

3. ::first-letter

This is useful when you want to style the first letter of a block-level element. Note that this will only work when the first letter is not preceded by a media content e.g an image.

It is denoted by selector::first-letter e.g p::first-letter, #home-page::first-letter (in the case of an id selector) etc.

<div>
<p>This is a simple paragraph</p>
</div>
p::first-letter {
font-size: 1.5rem;
font-weight: bold;
font-family: sans-serif;
border: 1px solid red;
}

Note: Not all CSS properties work on ::first-letter. Check out the list of all permitted properties on this MDN link.

4. ::marker

::marker pseudo-element targets markers of list items. The markers of any element with a display: list-item property e.g li tag will be selected by this pseudo-element.

The example below styles the markers of li:

<div>
<h1>Programming Languages</h1>
<ul>
<li>Typescript</li>
<li>Python</li>
<li>Fortran</li>
<li>Java</li>
<li>Rust</li>
</ul>
</div>
li {
padding: 10px;
}

li::marker {
content: "😀";
color: red;
}

li:hover::marker {
content: "";
}

As opposed to the default bulleted point marker that comes with the li element, using the content: “” property of the pseudo-element ::marker, we used an emoji instead.

You may observe the emoji disappears when we hover on the list items. This is because we reset the content property of our li:hover::marker to an empty value upon hover. i.e content:””

Note: You can use the content: “”property to insert a unique style of marker e.g image, emoji etc.

5. ::placeholder

The ::placeholder pseudo-element can be used to style the placeholder text of an <input /> or <textarea></textarea> element. This may come handy when trying to create some cool styles without necessarily bloating our HTML.

Example:

<div>
<input type="text" placeholder="enter your name">
</div>
input[type="text"] {
padding: 1rem;
border: none;
background-color: #eaeaef;
outline: none;
}

input::placeholder {
text-align: center;
text-transform: capitalize;
color: red;
}

input:focus::placeholder {
position: absolute;
top: 5px;
left: 5px;
font-size: 10px;
}

Not only were we able to change the colour of the placeholder text (enter your name), but the size of the font, as well as the position of the placeholder text when we focused on the input element. This portion of the code (input:focus::placeholder) applies the properties embedded in it when we focus on the <input /> element.

6. ::file-selector-button

We can style the button of an input element of type file. i.e <input type="file" /> using ::file-selector-button.

<div>
<h1>Choose your avatar</h1>
<input type="file">
</div>
input::file-selector-button {
border: none;
background-color: #eae;
color: #fff;
cursor: pointer;
padding: 0.5rem;
}

Using the ::file-selector-button pseudo-element, we got rid of the default border of the button, gave it background colour, changed the text colour and added some padding.

We can also use it to create a more custom <input type="file" /> element.

Consider the example below:

div {
height: 50px;
}

input {
width: 30px;
height: 30px;
background-color: red;
border-radius: 50%;
padding: 1rem;
}

input::file-selector-button {
border: none;
background-color: #eae;
color: #fff1;
margin-right: 2rem;
width: inherit;
height: inherit;
clip-path: polygon(10% 31%, 35% 30%, 35% 0%, 65% 0%, 65% 31%, 91% 31%, 91% 59%, 65% 59%, 65% 100%, 36% 100%, 36% 60%, 10% 60%);
}

The next chapter of this article will cover pseudo-classes in details.

As Madam Curie said “Nothing in life is to be feared, it is only to be understood.” Just keep learning and practising. I promise you will get better in no time.

Thanks for reading. Remember to drop your comment. Feedback helps me know to how to better structure, and convey my knowledge.

You can connect with me on Twitter @codesader

--

--