A pseudo-element in CSS is a virtual part of an element that we can style without adding a real tag to the HTML.
If pseudo-classes (:hover, :focus, :nth-child) are used to select an element based on its state or position, pseudo-elements are used to target specific fragments or pieces generated by CSS.
! symbol was placed by CSSYou will recognize them because they are written with double colons (::). In this post we will focus on the two most used ones: ::before and ::after.
::before and ::after
::before creates virtual content before the real content of the element. ::after creates it after the real content.
<p class="mensaje">Hola mundo</p>
.mensaje::before {
content: "👉 ";
}
.mensaje::after {
content: " ✅";
}
Visually, the browser will display something like:
👉 Hola mundo ✅
In the HTML, those two emojis do not exist. CSS generated them at the moment of painting the page.
For years you will see examples with a single : (:before, :after). Today, it is correct to use ::before and ::after, because we are talking about pseudo-elements, not pseudo-classes.
The content property
For ::before and ::after to exist, they need the content property. It can contain text, an empty string, or even values taken from attributes.
blockquote::before {
content: "«";
font-size: 2rem;
color: #94a3b8;
}
blockquote::after {
content: "»";
font-size: 2rem;
color: #94a3b8;
}
If the pseudo-element is purely decorative, we use content: "".
h2::after {
content: "";
display: block;
width: 48px;
height: 4px;
margin-top: 0.5rem;
background: #2563eb;
}
Without content, ::before and ::after will not appear (empty content is valid)
You can have width, height, background, position, and a doctoral thesis written inside, but if content is missing, the pseudo-element will not be drawn.
Complete example: warning with icon
Let’s create a warning box with an icon generated by CSS, like the one we saw at the beginning of the article.
The important text is still in the HTML, and the icon is only visual support.
<p class="aviso">Revisa los campos obligatorios antes de enviar.</p>
.aviso {
position: relative;
padding: 1rem 1rem 1rem 3rem;
border-left: 4px solid #f59e0b;
background: #fffbeb;
}
.aviso::before {
content: "!";
position: absolute;
left: 1rem;
top: 50%;
width: 1.4rem;
height: 1.4rem;
border-radius: 50%;
background: #f59e0b;
color: white;
font-weight: bold;
text-align: center;
line-height: 1.4rem;
transform: translateY(-50%);
}
This pattern is repeated a lot in interfaces: icons, decorative lines, badges, arrows, separators… small visual pieces that do not deserve their own tag.
