::before & ::after (Pseudo-elements)
::before & ::after (Pseudo-elements)
Level 9 — Visual Effects & State CSS selectors that insert virtual, style-only elements into the page content tree before or after the target element's actual HTML content, without cluttering the HTML markup.
1. Prerequisites
- Selectors (Element, Class, ID) — Base class selectors.
:hover&:focus(Pseudo-classes) — Understanding single-colon states.
2. Term Category
CSS Selector Hook (Universal Modern Standard .): ::before & ::after (Pseudo-elements) is a fundamental concept in this technology stack. Level 9 — Visual Effects & State
3. Explanation
(1) Design Motivation — "Why did we design this?"
When designing user interfaces, you often need to add purely decorative visual elements:
- An accent color bar underneath a page heading.
- A small folder icon in front of a list item.
- A decorative background color overlay on top of a banner.
If you write these decorations directly in your HTML using empty tags (like <span class="accent-line"></span>), you clutter your code.
HTML should only represent the logical structure of the page; visual decorations should be managed entirely in CSS.
To solve this, the W3C created Pseudo-elements (specifically ::before and ::after).
They let you generate styling nodes out of thin air using CSS, keeping your HTML clean, readable, and accessible.
(2) The Mandatory content Property
For a pseudo-element to render on the page, it must declare a content property. If you omit content, the pseudo-element is completely ignored by the browser:
/* BAD: Nothing displays! */
.invalid-heading::before {
background-color: red;
width: 50px;
height: 5px;
}
/* GOOD: Declaring the content key renders the box! */
.valid-heading::before {
content: ""; /* Empty quotes work if you just want a colored box */
background-color: red;
width: 50px;
height: 5px;
}
(3) Single Colon (:) vs. Double Colon (::)
- Pseudo-classes (Single Colon, e.g.
:hover): Select existing elements when they enter a specific state. - Pseudo-elements (Double Colon, e.g.
::before): Generate new virtual elements in the document tree.
Note: Older CSS standards used a single colon for both. Modern browsers still support :before for backwards compatibility, but you should always write ::before in modern code.
(4) Code Examples
Short Snippet
Adding a decorative symbol prefix:
.external-link::after {
content: " ↗"; /* Inserts an arrow character after the link text */
font-weight: bold;
color: gray;
}
Fuller Example (Accent Line Header)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pseudo-elements Demo</title>
<style>
body {
font-family: sans-serif;
margin: 40px;
background-color: #fafafa;
}
.section-title {
position: relative;
color: #333;
padding-bottom: 12px;
}
/* GENERATE AN ACCENT DECORATION UNDER THE HEADER */
.section-title::after {
content: ""; /* Required! */
position: absolute;
bottom: 0;
left: 0;
width: 60px;
height: 4px;
background-color: #ff007f;
border-radius: 2px;
}
.btn {
position: relative;
padding: 12px 24px;
border: 2px solid #333;
background: none;
font-weight: bold;
cursor: pointer;
}
</style>
</head>
<body>
<h2 class="section-title">Decorated Section Title</h2>
<p>The pink underline under the title above is not in the HTML markup. It is generated purely by CSS using an ::after pseudo-element!</p>
</body>
</html>
4. Common Mistakes & Pitfalls
Mistake 1: Setting dimensions on an inline pseudo-element
The mistake: Declaring ::before { content: ""; width: 50px; height: 50px; } and wondering why the element has zero size.
Why it's wrong: Pseudo-elements default to display: inline by default. Inline elements ignore width and height settings.
Fix: Change the display type to block or inline-block: ::before { content: ""; display: block; width: 50px; height: 50px; }.
Mistake 2: Omitting Mandatory content: "" Property in ::before and ::after Pseudo-Elements
The mistake: Creating ::before pseudo-elements omitting content: "".
Why it's wrong: Without the content property (even an empty string content: ""), browsers do NOT render ::before or ::after pseudo-elements on screen.
Incorrect:
.card::before { background: red; width: 10px; } /* ❌ Missing content property! Nothing renders! */
Fix:
.card::before {
content: ""; /* Mandatory content property */
background: red;
width: 10px;
}
Mistake 3: Attempting to Apply ::before or ::after to Void Elements (<img>, <input>)
The mistake: Adding img::before or input::after in CSS.
Why it's wrong: Pseudo-elements insert content INSIDE the target element container. Void elements (<img>, <input>, <hr>) cannot contain child content, so pseudo-elements fail.
Incorrect:
img::after { content: "Caption"; } /* ❌ Void elements cannot have pseudo-elements! */
Fix:
/* Wrap img in container div and apply pseudo-element to container: */
.img-wrapper::after { content: "Caption"; }
5. Practice Exercises
Exercise 1: Custom Decorative Icon Accents using ::before and ::after
Scenario: An author adds a decorative bullet icon before list items using ::before and content: "".
Requirements:
- Target
.custom-list li::before. - Set
content: "". - Set
display: inline-blockand dimensions.
Answer
Implementation
.custom-list {
list-style: none;
padding: 0;
}
.custom-list li {
position: relative;
padding-left: 1.5rem;
margin-bottom: 0.5rem;
}
/* Decorative Custom Bullet Accent */
.custom-list li::before {
content: ""; /* MANDATORY: Creates pseudo-element box */
position: absolute;
left: 0;
top: 0.5rem;
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background-color: #2563eb;
}
Technical Explanation
- The
::before&::afterPseudo-Elements: Insert decorative inline box elements into the DOM before or after an element's actual content. - Mandatory
contentProperty: Pseudo-elements WILL NOT RENDER unlesscontent: ""is explicitly declared (even if empty string). - Decorative HTML Cleanup: Keeps decorative visual accents (bullets, badges, lines) strictly in CSS, keeping HTML clean and semantic.
Exercise 2: Custom Text Selection Highlights with ::selection
Scenario: Customizes visual text highlight colors when users select text on page using ::selection.
Requirements:
- Apply
::selectionbackground and text color.
Answer
Implementation
/* Global Text Selection Style */
::selection {
background-color: #2563eb; /* Primary brand blue background fill */
color: #ffffff; /* Crisp white text color */
}
Technical Explanation
- The
::selectionPseudo-Element: Applies styles to the portion of an element that is highlighted/selected by the user's cursor. - Brand Polish: Replaces default blue browser selection colors with custom brand colors.
- Property Restrictions: Only supports
color,background-color, andtext-shadowfor security and performance.
Exercise 3: Styling Input Placeholders with ::placeholder
Scenario: Styles muted input placeholder text using ::placeholder.
Requirements:
- Apply
color: #94a3b8andfont-style: italicto::placeholder.
Answer
Implementation
.form-input::placeholder {
color: #94a3b8; /* Muted slate text color */
font-style: italic;
opacity: 1; /* Fixes Firefox default placeholder opacity reduction */
}
Technical Explanation
- The
::placeholderPseudo-Element: Targets placeholder text rendered inside<input>or<textarea>elements. - Firefox Opacity Fix: Firefox defaults
::placeholdertoopacity: 0.54; always addopacity: 1when setting custom placeholder colors. - WCAG Contrast Requirement: Ensure placeholder text color maintains sufficient contrast while remaining visually distinct from typed input text.
6. Related Terms
:hover&:focus(Pseudo-classes) — Interactive state selectors.display: blockvsinlinevsinline-block— Overriding default inline display constraints.- Advanced Pseudo-classes — Related concept: Advanced Pseudo-classes.
7. Key Takeaways
- Pseudo-elements generate virtual layout nodes inside parent elements.
- Always declare the mandatory
contentproperty for pseudo-elements to render. - Use
::beforeto insert decorations first, and::afterto insert last. - Pseudo-elements default to
display: inline(override this to set width/height). - Use pseudo-elements to keep decorative markup out of semantic HTML.