Pseudo-elements let you style a part of an element, such as its first letter, its placeholder text or the bullet of a list item. They can even add extra decorative boxes that don't exist in your HTML. Pseudo-elements use two colons, as in ::before and ::first-letter.
Their biggest benefit is keeping your HTML clean. Decorative details, such as a quotation mark before a quote, a small arrow next to a link or a coloured line under a heading, can live entirely in CSS. The HTML stays focused on content, and the decoration can be changed in one place.
::before and ::after
These create a virtual child element at the start or end of an element's content. They must have a content property, even if it's empty:
.price::before { content: "£"; }
.new::after { content: " 🔥"; }
.decor::before { content: ""; display: block; width: 40px; height: 4px; background: gold; }With an empty content: "" and some sizing, they become decorative shapes such as underlines, ribbons, overlays and arrows. Combine them with position: absolute inside a position: relative parent to place them exactly where you want.
Styling parts of text and elements
| Pseudo-element | Styles |
|---|---|
::first-letter | The first letter, for drop caps |
::first-line | The first line of a paragraph |
::placeholder | Placeholder text in inputs |
::selection | Text the visitor highlights |
::marker | List bullets and numbers |
::backdrop | The background behind a <dialog> modal |
::first-line adapts as the screen width changes. Whatever text happens to fit on the first line gets the style, which you couldn't achieve by wrapping words in a <span>.
Accessibility and generated content
Text added with content is visible on screen and is usually read by screen readers too, but it can't be selected or copied, and search engines may ignore it. Use pseudo-elements for decoration: quote marks, icons, arrows, separators, shapes and overlays. Real information, such as prices, labels or warnings, belongs in the HTML. If a decorative symbol shouldn't be read aloud, you can give it empty alternative text with a slash: content: "★" / "";. The part after the slash is what assistive technology announces.
Positioning pseudo-elements
::before and ::after are inline by default. To use them as shapes, badges or overlays, give them display: block or position: absolute, plus a width and height. For absolute positioning, set position: relative on the parent so the pseudo-element is placed relative to it. A common pattern is a full-size overlay: .card::after { content: ""; position: absolute; inset: 0; }, which can darken an image or make a whole card clickable.
Pseudo-elements vs pseudo-classes
The two are easy to confuse:
- A pseudo-class (one colon) selects a whole element in a certain state, like
a:hover. - A pseudo-element (two colons) targets a part of an element or adds a new part, like
p::first-line.
You can combine them: a:hover::after styles the ::after content of a link while it's being hovered over. Older code sometimes writes pseudo-elements with one colon, such as :before. Browsers still accept that, but two colons is the modern standard.
CSS Pseudo-elements Syntax
Pseudo-element syntax
.box::before {
content: "";
/* sizes and styles for the virtual element */
}
p::first-letter { font-size: 3em; }
input::placeholder { color: #999; }CSS Pseudo-elements Summary
- Pseudo-elements use two colons and style part of an element.
::beforeand::afteradd content and need acontentproperty.::first-letter,::first-line,::placeholder,::selectionand::markerstyle specific parts.- Use generated content only for decoration, never for important information.
- Pseudo-classes select states; pseudo-elements select parts.
CSS Pseudo-elements Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Decorative content and shapes
<p class="price">49</p>
<h2 class="fancy">Featured</h2>
<a class="ext" href="#">Documentation</a>.price::before {
content: "£";
color: #17a865;
font-weight: bold;
}
.fancy::after {
content: "";
display: block;
width: 60px;
height: 5px;
margin-top: 6px;
border-radius: 3px;
background: #ffd23f;
}
.ext::after {
content: " ↗";
}Line-by-line explanation
- Lines 1 to 5Adds a green "£" before the number, without touching the HTML.
- Lines 7 to 15An empty pseudo-element becomes a short yellow underline bar.
- Lines 17 to 19An arrow after links to hint they go somewhere else.
Drop caps and styled lists
<p class="story">Once upon a time, a tiny sprout pushed through the soil and decided it would one day become the tallest tree in the forest.</p>
<ul class="checks">
<li>Water daily</li>
<li>Plenty of sun</li>
</ul>.story::first-letter {
float: left;
font: 700 3.2em/0.9 Georgia, serif;
color: #7c3aed;
margin-right: 6px;
}
.story::first-line {
font-variant: small-caps;
}
.checks li::marker {
content: "✅ ";
}Line-by-line explanation
- Lines 1 to 6A magazine-style drop cap: big, floated left.
- Lines 8 to 10The first line in small capitals. It adapts as the width changes.
- Lines 12 to 14Replace the bullet points with emoji.
Placeholders and selection colour
Select some text in the result to see the custom highlight.
<input placeholder="Search recipes…">
<p>Select this sentence to see the custom highlight colour.</p>input {
padding: 10px;
border: 2px solid #15161b;
border-radius: 10px;
}
input::placeholder {
color: #f0592b;
font-style: italic;
}
::selection {
background: #ffd23f;
color: #15161b;
}Line-by-line explanation
- Lines 7 to 10Style the hint text inside the empty input.
- Lines 12 to 15The colour of highlighted text across the whole page.
Common CSS Pseudo-elements Mistakes
Forgetting content
.badge::after {
width: 10px; height: 10px; background: red;
}.badge::after {
content: "";
display: inline-block;
width: 10px; height: 10px; background: red;
}Without content, the pseudo-element is never created. And it's inline by default, so give it a display if you size it.
Using pseudo-elements on void elements
img::after { content: "Photo"; }
input::before { content: "→"; }.photo-wrap::after { content: "Photo"; }Elements without content (img, input, br) can't have ::before/::after. Wrap them instead.
CSS Pseudo-elements in Real Projects
Hidden helpers everywhere
Tooltips' little arrows, the coloured bar under a heading, quote marks on testimonials, required-field asterisks (label.required::after { content: " *"; }), image overlays and custom checkbox ticks are almost always ::before or ::after.
CSS Pseudo-elements Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Add a star after every .fav element.
.fav {
: " ★";
}
::after plus a content value.How many colons do pseudo-elements use in modern CSS?
::before. The two colons distinguish pseudo-elements from pseudo-classes.Which property is required for ::before to appear?
content, no pseudo-element.Required labels should show a red asterisk after the text. Fix the CSS.
<label class="required">Email</label>.required:after {
color: red;
}- The asterisk appears
- It's red
CSS Pseudo-elements Quiz
3 questions to check what you learned.
Which pseudo-element styles the first letter of a paragraph?
- :first-letter-only
- ::letter(1)
- ::first-letter
- ::initial
Which pseudo-element styles list bullets?
- ::bullet
- ::list
- ::point
- ::marker
Can you put
::afteron an<img>?- Yes, always
- Only with display: block
- Only in Firefox
- No, because void elements have no content box for it
CSS Pseudo-elements Challenge
Quote decoration
Decorate the testimonial:
.quote::beforeshows a big“(content"“") in#4467ff.quote::aftershows”.quote::first-lineis bold
<blockquote class="quote">This site made CSS finally make sense. I built my portfolio in a week!</blockquote>.quote { font: 1.1rem/1.6 Georgia, serif; max-width: 360px; }
- Opening quote added
- Opening quote is blue
- Closing quote added
- A ::first-line rule makes it bold