Two pseudo-elements show up almost everywhere in modern stylesheets: ::before and ::after. They add a rendered fragment right before or right after an element's real content, without ever touching the HTML.
Decorative icons, list counters, the old-school clearfix, or tooltips built entirely in CSS: these two pseudo-elements cover an impressive range of needs for a tool that looks so simple on the surface.
Definition
::before inserts generated content right before an element's real content, inside its box. ::after inserts that content right after. In both cases, that content does not exist in the HTML: it is entirely produced by CSS at render time.
The one requirement: content
Without the content property, neither pseudo-element shows up, even if every other property is correctly set. An empty value is enough to make the generated box exist.
/* decorative icon before a badge */
.badge::before {
content: "★";
margin-right: 4px;
color: gold;
}
/* old-school clearfix after a container */
.container::after {
content: "";
display: table;
clear: both;
}
/* automatic counter inside a list */
.steps {
counter-reset: step;
}
.steps li::before {
counter-increment: step;
content: "Step " counter(step) ": ";
font-weight: bold;
}The third example shows a lesser known use: ::before can display a native CSS counter, incremented automatically for every item in the list, without hardcoding a single number in the HTML.
Common use cases
The clearfix, an older technique still found in existing code, uses ::after to force a container to wrap elements set to float. Pure CSS tooltips combine ::after with a parent element's :hover state to display text on hover, with no dependency on JavaScript. Decorative icons avoid adding an extra tag just for a purely visual symbol.
| Use | Pseudo-element |
|---|---|
| Icon before a piece of text | ::before |
| Arrow or symbol after a link | ::after |
| Clearfix | ::after |
| Automatic counter | ::before |
::before or ::after is never read consistently by every screen reader, and it cannot be selected by a user. Save these pseudo-elements for decoration, never for information essential to understanding.Building a tooltip in pure CSS
A tooltip that appears on hover nicely illustrates how ::after pairs with a Pseudo-class like :hover. The pseudo-element supplies the text to display through content, and the pseudo-class controls its visibility based on the parent element's hover state, with no JavaScript needed for this behavior even though it looks dynamic.
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-text);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
}
.tooltip:hover::after {
opacity: 1;
}The attr() function reads the value of a data-* attribute directly from the HTML, which makes it possible to reuse the same CSS block for any tooltip text, simply by changing the attribute's value on each relevant element.
Frequently asked questions
::before?Yes, through content: url("image.png"), or more often by leaving content empty and applying a background-image to the pseudo-element instead, for more control over its size and position. This second method also makes it easier to use background-size and background-position to fine-tune the final rendering based on the generated box's dimensions.
::before and ::after work on any tag?No, they do not apply to so-called replaced elements, such as img or input, since these tags have no internal content to insert anything into.
content text include quotes or apostrophes?Yes, as long as they are escaped properly or the other quote type is used to wrap the value, for example content: "the example's" with double quotes around a single apostrophe. In practice, it is often simpler to consistently wrap the value in double quotes, escaping a lone apostrophe with a backslash when needed.