::before & ::after
::before and ::after are generated-content pseudo-elements. They insert an extra, purely visual "box" immediately before or after an element's actual content, without needing any additional markup. They are enormously useful for decoration, but they come with one strict rule that trips up almost everyone at least once.
The content property is required
/* This renders NOTHING — no "content" property */
.card::before {
background: red;
width: 20px;
height: 20px;
}
/* This renders a small red decorative box */
.card::before {
content: '';
display: block;
background: red;
width: 20px;
height: 20px;
}Classic use cases
Decorative icons/marks — inserting a visual symbol without adding a meaningless element to the HTML.
The clearfix hack — an older technique (
.clearfix::after { content: ""; display: table; clear: both; }) used to contain floated children before flexbox/grid made floats for layout mostly obsolete. Rarely needed today, but you'll still see it in legacy code.Tooltip arrows — a small rotated square positioned with
::afterto create the little triangle pointing from a tooltip to its target.Numbered counters — combining
::beforewith CSS counters to auto-number sections, steps, or list items.
Worked example: a tooltip with an arrow
.tooltip {
position: relative;
background: #1f2937;
color: white;
padding: 0.5rem 0.75rem;
border-radius: 6px;
font-size: 0.875rem;
}
/* The little arrow pointing down at the target element */
.tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #1f2937;
}Worked example: numbered steps with counters
.steps {
counter-reset: step;
list-style: none;
padding: 0;
}
.steps li {
counter-increment: step;
position: relative;
padding-left: 2.5rem;
margin-bottom: 1rem;
}
.steps li::before {
content: counter(step);
position: absolute;
left: 0;
top: 0;
width: 1.75rem;
height: 1.75rem;
border-radius: 50%;
background: #2563eb;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
font-weight: 600;
}A few other things worth knowing: ::before is inserted as the first child of the element's content, and ::after as the last, so they participate in layout alongside real content unless you position them out of flow. They also inherit properties from the host element the same way a real child element would.