In short
CSS pseudo-elements let you style a specific part of an element, like its first letter or its placeholder text, or add decorative content with ::before and ::after. What is the difference between a pseudo-class and a pseudo-element?
CSS pseudo-elements let you style a specific part of an element, like its first letter or its placeholder text, or add decorative content with ::before and ::after.
What is the difference between a pseudo-class and a pseudo-element?
A pseudo-class selects a whole element in a certain state, like :hover. A pseudo-element selects or creates a part of an element, like the first line of a paragraph or an extra box before its content.
Pseudo-elements use two colons: ::before, ::after, ::placeholder. You may see old code with one colon, like :before. Browsers still accept it for a few old pseudo-elements, but two colons is the modern, correct way.
| Pseudo-element | What it selects |
|---|---|
::before | A new box inserted at the start of the element's content |
::after | A new box inserted at the end of the element's content |
::placeholder | The placeholder text in an input or textarea |
::first-letter | The first letter of a block of text |
::first-line | The first line of a block of text |
::selection | Text the user has highlighted with the mouse |
::marker | The bullet or number of a list item |
How do ::before and ::after work?
::before and ::after create a small extra box inside the element: one before its content and one after it. They only appear if you give them the content property. The content can be text, an empty string "", or a value like attr().
Think of them as two free stickers every element gets. You can put a word, an icon or a coloured shape on each sticker.
<p class="note">Save your work before you close the editor.</p>
.note {
background: #eff6ff;
border-left: 4px solid #2563eb;
padding: 12px 16px;
}
.note::before {
content: "Note: ";
font-weight: bold;
color: #2563eb;
}
The paragraph shows "Note: Save your work before you close the editor." The word "Note:" is not in the HTML, so you never need to type it again. Every .note paragraph gets it automatically.
Common mistake: Forgetting
content. Without it,::beforeand::afterdo not appear at all, even if you give them a size and background. For a purely decorative shape, usecontent: "";.
Note: Text added with
contentcannot be selected or copied, and some screen readers skip it. Only use it for decoration or small labels, never for important information.
Decorative icons and badges with ::after
Pseudo-elements are great for small visual extras that don't belong in the HTML. Here is a "NEW" badge and an arrow on a link:
<a class="course-link new" href="/css-grid">CSS Grid course</a>
.course-link {
color: #1f2937;
font-weight: 600;
text-decoration: none;
}
.course-link::after {
content: " →";
display: inline-block;
transition: transform 0.2s;
}
.course-link:hover::after {
transform: translateX(4px);
}
.new::before {
content: "NEW";
background: #dc2626;
color: white;
font-size: 0.7rem;
padding: 2px 6px;
border-radius: 4px;
margin-right: 8px;
vertical-align: middle;
}
The link shows a small red "NEW" badge before the text and an arrow after it. When you hover the link, the arrow slides a little to the right. Notice the selector .course-link:hover::after: a pseudo-class comes first, then the pseudo-element.
You can also use content: attr(name) to show an attribute's value. For example, abbr::after { content: " (" attr(title) ")"; } prints the full meaning after an abbreviation.
Styling text parts: ::first-letter, ::first-line and ::selection
These pseudo-elements style part of the text without extra <span> tags:
.story::first-letter {
float: left;
font-size: 3.5rem;
line-height: 1;
font-weight: bold;
color: #7c3aed;
margin-right: 8px;
}
.story::first-line {
font-variant: small-caps;
}
::selection {
background: #fde68a;
color: #1f2937;
}
The first letter of each .story paragraph becomes a big purple "drop cap", like in a printed magazine. The first line appears in small capital letters, and it adjusts by itself when the window is resized. When you highlight any text on the page, the highlight is soft yellow instead of the default blue.
Styling placeholders and list markers
The ::placeholder pseudo-element styles the hint text inside an empty input. The ::marker pseudo-element styles the bullets or numbers of list items.
<input class="search" type="search" placeholder="Search lessons...">
<ol class="steps">
<li>Install a code editor</li>
<li>Create an index.html file</li>
<li>Open it in your browser</li>
</ol>
.search::placeholder {
color: #9ca3af;
font-style: italic;
}
.steps li::marker {
color: #16a34a;
font-weight: bold;
}
The placeholder text "Search lessons..." appears in light grey italics. The numbers 1, 2 and 3 in the list turn green and bold, while the list text stays normal. Before ::marker existed, you needed tricks with ::before to do this.
Tip: Keep placeholder text readable. Very light grey looks elegant but can be hard to read, and a placeholder is not a replacement for a
<label>.
Practice exercise
Create a blog post title <h2 class="post-title"> and a quote <blockquote class="quote">. Use ::before to put a small coloured line above the title, and use ::before and ::after to add big curly quote marks around the quote. Finally, give the page a custom ::selection color.
Hint: for the line, use content: ""; with display: block, a width, a height and a background. For quote marks, use content: "“"; and content: "”";.
Key takeaways
- Pseudo-elements use two colons and select or create a part of an element.
::beforeand::afterneed thecontentproperty to appear.- Use generated content for decoration, not for important information.
::first-letter,::first-lineand::selectionstyle parts of text without extra HTML.::placeholderstyles hint text in inputs, and::markerstyles list bullets and numbers.