Computer scienceFrontendCSSSelectors in Depth

Pseudo-elements

12 minutes read

The Almighty CSS allows you not only to work with the elements declared in your HTML code but also to customize those parts of the page you can't address using simple selectors. This might be the customization of the first line of the paragraph, the first letter of the paragraph, or even the part of the text a user has just selected. Let’s take a look through the most common pseudo-elements and find out when and how to properly use them.

Decorating the first line and the first letter

The ::first-letter and ::first-line pseudo-elements allow you to customize - as it’s obvious from their names - the first letter or the first line of the text inside the element. Let’s take a look at the example right away!

<div class="container">
    <p class="paragraph1">Oh my god! What just happened to my first letter?</p>
    <p class="paragraph2">I think our programmer is experimenting with different CSS properties, so 
    nevermind. That's gonna be fine. <br>And i think that's happening to me as well..</p>
</div>
.container {
  line-height: 3em;
  padding: 3em;
  width: 50vw;
}

.paragraph1,
.paragraph2 {
  color: black;
  font-size: 1.5em;
}

.paragraph1::first-letter {
  color: red;
  font-size: 4em;
}

.paragraph2::first-line {
  color: blue;
}

And the result is gonna be the following:

First letter is red and big

There are two important things that you should know about those pseudo-elements. The first one is that the length of the line might change depending on the width of the screen. If you want to limit the line, you should do it manually in HTML using the <br> tag:

<div class="container">
    <p class="paragraph1">Oh my god! What just happened to my first letter?</p>
    <p class="paragraph2">I think our programmer 
    <br>
    is experimenting with different CSS properties, so nevermind. That's gonna be fine. <br>And i think that's happening to me as well..</p>
</div>

First letter of an article is big and red

The second important thing is that you can only use all the text-related properties such as color, font-size, font-family etc. for both ::first-line and ::first-letter. All the other properties will be ignored.

Notice, that if ::first-line and ::first-letter are applied at the same time, regardless of the order the first letter will stick to the rules stated in ::first-letter.

As you can see, the syntax rules for pseudo-elements are very similar to the syntax of pseudo-classes. There are just two colons instead of one, and that's it!

Customizing placeholder

The mission of the ::placeholder pseudo-element is again pretty obvious from its name.

Just in case: it's used to define styles for the placeholder text inside the <input> or <textarea> tags. You might want to change the default light-gray placeholder text color into something you'd like while customizing your forms, so this pseudo-element is sometimes insanely useful. And here is an example:

<input class="custom_input" type="email" placeholder="Enter your email">
.custom_input {
  border: 1px solid red;
  height: 3em;
  margin: 3em 0 0 3em;
  padding: 1em;
  width: 15em;
}

.custom_input::placeholder {
  color: #ff8b8b;
}

Red email input

The amount of properties you can use to customize a placeholder is also limited. As with ::first-line and ::first-letter, it's only possible to use text-related ones.

Note that the text you type inside the input is not going to be the same color as the placeholder. To change the color of the text you may wanna use the color property for the input selector itself.

Customizing markers

The ::marker pseudo-element is used to customize markers in unordered lists. By default, those markers are just dots of the same color as the text, but using this pseudo-element you can change their color, size, and even their shape:

<ul class="my_list">
    <li>::first-letter</li>
    <li>::first-line</li>
    <li>::placeholder</li>
    <li>::marker</li>
    <li>::before</li>
    <li>::after</li>
    <li>::selection</li>
</ul>
.my_list {
  margin:3em;
}

.my_list li::marker {
  color: red;
  font-size: 1.5em;
}

The list is markered by red circles

In addition to all the text-related properties, it's also possible to animate markers using animation-name, animation-duration, transform etc.

But there is also one yet unknown property you can use with ::marker called content, which sets the text instead the default dot:

<ul class="my_list">
    <li>::first-letter</li>
    <li>::first-line</li>
    <li>::placeholder</li>
    <li>::marker</li>
    <li>::before</li>
    <li>::after</li>
    <li>::selection</li>
</ul>
.my_list {
  margin:3em;
}

.my_list li::marker {
  color: red;
  content: "> ";
}

The list has marks as red arrows

Before and after?

The ::before and ::after pseudo-elements allow you to define the content that for ::before will take place before the actual content stated in HTML, and after the actual content for ::after.

You are completely free to use any properties you'd like customizing ::before and ::after, and that's exactly why those pseudo-elements are used pretty often. Despite the fact that you thought those were useless at the beginning. You did, right?

Let's take a look at one simple block:

<div class="container">
    <p>That's the thing about people who think they hate computers... </p>
    <p>What they really hate are lousy programmers.</p>
    <p class="small">Larry Niven</p>
</div>
.container {
    font-size: 1.5em;
    margin: 2em 0 0 25vw;
    padding: 1em;
    position: relative;
    width: 50vw;
}

.small {
  color: gray;
  font-size: .7em;
}

And this what we'll get:

Article

Now let's apply ::before and ::after to make it look fancy! Here is the code:

<div class="container">
    <p>That's the thing about people who think they hate computers... </p>
    <p>What they really hate are lousy programmers.</p>
    <p class="small">Larry Niven</p>
</div>
.container {
    font-size: 1.5em;
    padding: 1em;
    position: relative;
    margin: 2em 0 0 25vw;
    width: 50vw;
}

.container::before,
.container::after {
    position: absolute;
    top: 1em;
    font-size: 2em;
    background: #e9e9e9;
    padding: .2em;
    line-height: 2.5em;

}

.container::before {
    content: open-quote;
    left: -50px;
}

.container::after {
    content: close-quote;
    right: -50px;
}

.small {
  color: gray;
  font-size: .7em;
}

And here is the result:

Quote article

As you can see, here we used all kinds of different properties, even the position property. So, consider those pseudos as usual elements without HTML declaration, and feel free to customize them however you want. The only rule you have to follow is that the content property must always be defined. If you want no content there, just make the value an empty string: content: "".

Filter

Now let's move on to another case that you either already had to solve or will have to in the nearest future. As you may know, the filter property defined for the parental block also affects its child blocks:

<div class="container">
  <div class="container_inside">
    <p>Any fool can write code that a computer can understand. Good programmers write code that humans can understand.</p>
    <p class="small">Martin Fowler</p>
  </div>
</div>
.container {
  width: 50vw;
  height: 80vh;
  margin: 10vh 0 0 25vw;
  background: url('https://i.ibb.co/ngx7VSR/photo-2021-04-19-21-24-36.jpg');
  background-position: center;
  background-size: cover;
  color: white;

  filter: brightness(.5);
}

.container_inside {
  align-items: center;
  display: flex;
  flex-direction: column;
  font-size: 1.5em;
  height: calc(100% - 4em);
  justify-content: center;
  padding: 2em;
  text-align: center;
  width: calc(100% - 4em);
}

.small {
  color: #e9e9e9;
  font-size: .7em;
}

The text on the photo

As you can see from CSS, the text inside the block is supposed to be white, but it's not because even though filter property is applied to the .container, its .container_inside child is affected as well.

To avoid that we will use the ::before pseudo-element as a container for the content we want to apply the filter to:

<div class="container">
  <div class="container_inside">
    <p>Any fool can write code that a computer can understand. Good programmers write code that humans can understand.</p>
    <p class="small">Martin Fowler</p>
  </div>
</div>
.container {
  color: white;
  height: 80vh;
  margin: 10vh 0 0 25vw;
  position: relative;
  width: 50vw;
}

.container::before {
  background: url('https://i.ibb.co/ngx7VSR/photo-2021-04-19-21-24-36.jpg');
  background-position: center;
  background-size: cover;

  content: "";

  filter: brightness(.5);

  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;

  z-index: -1;
}

.container_inside {
  align-items: center;
  display: flex;
  flex-direction: column;
  font-size: 1.5em;
  height: calc(100% - 4em);
  justify-content: center;
  padding: 2em;
  text-align: center;
  width: calc(100% - 4em);
}

.small {
  color: #e9e9e9;
  font-size: .7em;
}

White text on the photo

Here the background and filter properties are applied to the .container::before pseudo-element, which makes all the elements on the inside unaffected by our annoying little filter.

Notice that the same thing could be done using ::after instead of ::before. Try to change it yourself to see what happens.

Selected text

This pseudo-element is an experimental feature. It might not work in different browsers, so don't forget to check if it does. Also, note that its behavior and syntax might change by the time this feature becomes a standard.

The ::selection pseudo-element allows you to define the styles for the part of the HTML document a user has currently selected. It does not help solving any important cases, but it's always good to make a web-site stand out.

Due to the fact that this feature is still in developing, there's only a short list of properties you can use with it:

  • color

  • background-color

  • cursor

  • caret-color

  • outline (and other connected properties)

  • text-decoration (and other connected properties)

  • text-emphasis-color

  • text-shadow

And here is an example of a fancy selected text:

<div class="container">
    <p class="my_paragraph">"The trouble with programmers is that you can never tell what a programmer is doing until it's too late", - Seymour Cray</p>
</div>
.container {
  font-size: 2em;
  margin: 3em;
  width: 50vw;
}

.my_paragraph::selection {
    background: #6d6d6d;
    color: white;
    text-shadow: 1px 1px 10px white;
}

The article with highlighted text

Summary

Pseudo-elements are used to work with elements not stated directly in the HTML. Some of them provide easier ways to do certain things such as customizing the first letter or the first line of the paragraph or the selected text, but some of them are the helping hand in different kinds of situations that are very likely gonna appear while building a web-site.

In this topic we looked through the most common pseudo-elements, but there are more. You can find the full list of pseudo-elements here.

62 learners liked this piece of theory. 4 didn't like it. What about you?
Report a typo