Introduction to Pseudo-elements
In our previous lectures, we explored various ways to select and style HTML elements based on their attributes, states, and relationships. Pseudo-elements take this a step further by allowing us to style parts of elements that don't actually exist in the HTML document tree.
Pseudo-elements create "virtual" elements that can be styled as if they were actual elements in the document. This powerful feature enables sophisticated styling effects without cluttering your HTML with extra markup.
"Pseudo-elements are like secret compartments in your HTML elements—spaces where you can add visual details and content without modifying the structure."
While pseudo-classes (which we covered earlier) select elements based on their states or positions, pseudo-elements select specific parts of elements or generate entirely new content around them.
Pseudo-elements vs. Pseudo-classes
Before diving deeper, let's clarify the distinction between pseudo-elements and pseudo-classes:
| Pseudo-classes | Pseudo-elements |
|---|---|
| Select elements based on states or positions | Select parts of elements or create virtual elements |
Use a single colon (:) |
Use double colons (::) in modern syntax |
Examples: :hover, :first-child, :checked |
Examples: ::before, ::after, ::first-line |
| Select actual elements in different states | Select or generate "virtual" parts of elements |
Note on Syntax
Although the CSS3 specification introduced the double-colon notation (::) for pseudo-elements to distinguish them from pseudo-classes, the single-colon syntax is still accepted by browsers for backward compatibility. In modern CSS, it's best practice to use the double-colon for pseudo-elements.
Common Pseudo-elements
CSS defines several pseudo-elements, but we'll focus on the most commonly used and widely supported ones.
::before and ::after Pseudo-elements
The ::before and ::after pseudo-elements are the most versatile and frequently used. They create virtual elements as the first and last children of the selected element, respectively.
/* Basic usage with generated content */
.quote::before {
content: """; /* Opening quotation mark */
}
.quote::after {
content: """; /* Closing quotation mark */
}
/* Creating decorative elements */
.card::before {
content: "";
position: absolute;
top: -5px;
left: 10px;
width: 20px;
height: 20px;
background-color: #0066cc;
transform: rotate(45deg);
}
/* Adding icons with Unicode */
.external-link::after {
content: " \2197"; /* Unicode for "↗" */
font-size: 0.8em;
}
/* Using images as content */
.premium-badge::before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
background-image: url('star.svg');
background-size: contain;
margin-right: 5px;
}
The content Property
The content property is required for ::before and ::after pseudo-elements to be generated. Even if you don't want to add any text content, you must include content: "" for the pseudo-element to appear.
Visual Representation
❝ This paragraph has quotation marks added with pseudo-elements. ❞
HTML Structure
<p class="quote">This paragraph has quotation marks added with pseudo-elements.</p>
<div class="card">
Element with a decorative corner ribbon
</div>
<a href="#" class="external-link">External link</a>
Positioning ::before and ::after
By default, ::before and ::after elements are inline. To position them precisely, you can use:
.element::before {
content: "";
display: block; /* or inline-block, absolute, etc. */
position: absolute; /* if parent is positioned */
/* other positioning properties */
}
Common Use Cases for ::before and ::after
- Decorative elements: Icons, badges, ribbons, dividers
- Typography enhancements: Quotes, bullet points, numbering
- Visual indicators: Required form fields, validation states
- Interactive elements: Tooltips, dropdown arrows, toggle buttons
- Layout techniques: Clearfix, aspect ratios, grid overlays
- Animations and transitions: Hover effects, loading spinners
::first-line Pseudo-element
The ::first-line pseudo-element selects the first line of text in a block-level element. It's useful for creating typographic effects.
/* Style the first line of a paragraph */
p::first-line {
font-weight: bold;
color: #333;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Style the first line of an article introduction */
.article-intro::first-line {
font-size: 1.2em;
font-style: italic;
}
This would be the first line of text, which is styled differently from the rest of the paragraph. Notice how the first line has bold text, uppercase letters, and different letter spacing. The styling is dynamic and will adjust if the text reflows due to window resizing or other layout changes.
Dynamic Nature
The ::first-line pseudo-element is dynamic—the "first line" is recalculated whenever the text reflows due to window resizing or other layout changes. This makes it more flexible than manually wrapping the first line in a <span>.
Property Limitations
Only a subset of CSS properties can be used with ::first-line:
- Font properties (
font-size,font-weight, etc.) - Color properties
- Background properties
- Text properties (
text-decoration,text-transform, etc.) - Letter and word spacing
Layout properties like margin, padding, height, etc. won't have any effect.
::first-letter Pseudo-element
The ::first-letter pseudo-element selects the first letter of a block-level element. It's commonly used for creating drop caps and other typographic effects.
/* Basic drop cap */
p::first-letter {
font-size: 2.5em;
font-weight: bold;
float: left;
padding-right: 8px;
}
/* Fancy drop cap */
.article-intro::first-letter {
font-size: 3em;
font-family: 'Georgia', serif;
line-height: 0.8;
float: left;
padding: 4px 8px 0 0;
color: #900;
}
/* Art deco style initial cap */
.art-deco::first-letter {
font-size: 2em;
font-weight: bold;
border: 2px solid currentColor;
padding: 2px 10px;
margin-right: 6px;
border-radius: 50%;
background-color: #f0f0f0;
box-shadow: 2px 2px 0 rgba(0,0,0,0.1);
}
This paragraph demonstrates a basic drop cap effect using the ::first-letter pseudo-element. Notice how the first letter is larger and floated to the left, allowing the text to wrap around it. This technique is commonly used in magazines and books to add visual interest to the beginning of articles or chapters.
Fancy drop caps can be styled with different colors, fonts, and sizes to create more distinctive typographic effects. This example uses a serif font, red color, and adjusted line height to create a more traditional look reminiscent of illuminated manuscripts or classic book typography.
What Constitutes the "First Letter"?
The ::first-letter pseudo-element selects:
- The first letter of the first formatted line of a block element
- If preceded by punctuation, the punctuation is included
For example, in "Hello", ::first-letter selects H.
In "(Hello)", it selects (H.
Common Use Cases
- Drop caps: For article introductions, chapters, or stories
- Magazine-style typography: Creating visual interest in publications
- Decorative text effects: For headers, quotes, or featured text
- Brand styling: Emphasizing the first letter in brand names or slogans
::selection Pseudo-element
The ::selection pseudo-element applies styles to text that has been highlighted (selected) by the user.
/* Style all selected text */
::selection {
background-color: #4dabf7;
color: white;
}
/* Style selected text within specific elements */
.important-content::selection {
background-color: #ffd43b;
color: #212529;
}
/* Different selection styles for code blocks */
pre::selection, code::selection {
background-color: #212529;
color: #ffd43b;
}
Try selecting this text to see custom selection styling!
This paragraph could have different selection styling.
function example() {
// Code blocks might have distinctive selection colors
console.log("Try selecting this code!");
}
Property Limitations
Only certain CSS properties can be used with ::selection:
colorbackground-colortext-decorationand its sub-propertiestext-shadowstroke-color,fill-color, andstroke-widthfor SVG text
Other properties like font-size or text-transform won't have any effect.
Browser Support Note
Firefox used to require the -moz- prefix (::-moz-selection), but modern Firefox versions support the standard syntax. All modern browsers now support ::selection without a prefix.
::placeholder Pseudo-element
The ::placeholder pseudo-element selects the placeholder text in an input field or textarea.
/* Style all placeholders */
::placeholder {
color: #999;
font-style: italic;
}
/* Style placeholders in specific form fields */
.search-input::placeholder {
color: #6c757d;
opacity: 0.7;
}
/* Different placeholder styles for required fields */
input:required::placeholder {
color: #dc3545;
font-weight: bold;
}
In a real implementation, the second input would show different placeholder styling.
Browser Support Note
Some older browsers required vendor prefixes for ::placeholder:
::-webkit-input-placeholder { /* Chrome, Safari, Edge */
color: #999;
}
::-moz-placeholder { /* Firefox */
color: #999;
opacity: 1; /* Firefox applies opacity by default */
}
:-ms-input-placeholder { /* Internet Explorer */
color: #999;
}
::placeholder { /* Modern browsers */
color: #999;
}
For modern development in 2025, vendor prefixes are generally no longer needed for ::placeholder, but including them for very old browser support wouldn't hurt.
Common Use Cases
- Form styling: Making placeholders match design system
- User guidance: Styling placeholders to provide better visual cues
- Visual hierarchy: Reducing prominence of placeholder text
- Branding: Applying consistent brand styling to form elements
Other Pseudo-elements
CSS includes several other pseudo-elements with more specialized uses:
-
::marker - Styles the marker box of list items (bullets, numbers)
li::marker { color: #0066cc; font-weight: bold; } -
::file-selector-button - Styles the button part of
<input type="file">elementsinput[type="file"]::file-selector-button { background-color: #e9ecef; border: 1px solid #ced4da; border-radius: 4px; padding: 5px 10px; } -
::cue - Styles WebVTT cues in video elements
video::cue { background-color: rgba(0, 0, 0, 0.7); color: white; font-family: sans-serif; } -
::backdrop - Styles the backdrop of an element in fullscreen mode
dialog::backdrop { background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(3px); }
Browser Support
These newer pseudo-elements have varying levels of browser support. Always check current compatibility if using them in production.
Generated Content with the content Property
The content property, used with ::before and ::after pseudo-elements, is a powerful tool for adding content without modifying the HTML structure.
Text Content
/* Simple text content */
.new::after {
content: "NEW";
font-size: 0.8em;
background-color: #dc3545;
color: white;
padding: 2px 5px;
border-radius: 3px;
margin-left: 5px;
}
/* Multiple text strings */
.quote::before {
content: """ attr(cite);
}
/* Automatically add text based on element attributes */
a[href^="http"]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: #6c757d;
}
Product Feature NEW
This example shows a "NEW" badge added to an element without modifying the HTML.
This example shows how to display the URL for external links.
Special Values for the content Property
/* Empty string - creates the pseudo-element without text content */
.divider::before {
content: "";
display: block;
height: 1px;
background-color: #dee2e6;
margin: 20px 0;
}
/* URL - include an image */
.download::before {
content: url('download-icon.svg');
display: inline-block;
width: 16px;
height: 16px;
margin-right: 5px;
vertical-align: middle;
}
/* Attribute values */
a[title]::after {
content: " (" attr(title) ")";
}
/* Counters - for automatic numbering */
body {
counter-reset: section;
}
h2::before {
counter-increment: section;
content: "Section " counter(section) ": ";
}
/* Quotes - language-appropriate quotation marks */
q {
quotes: """ """ "'" "'";
}
q::before {
content: open-quote;
}
q::after {
content: close-quote;
}
Text above divider
Text below divider
The divider above is created using a pseudo-element, not an actual HTML element.
Using Counter Functions
CSS counters allow you to automatically number elements, creating complex numbering schemes without additional markup.
Practice Exercise
Pseudo-element Styling Challenge
Create a webpage that demonstrates your understanding of pseudo-elements and generated content through practical applications. Your page should include:
-
Typography Enhancements:
- Create at least two different styles of drop caps using
::first-letter - Style the first line of paragraphs in a distinct way with
::first-line - Add decorative quotation marks to blockquotes using
::beforeand::after
- Create at least two different styles of drop caps using
For each section, include a comment in your CSS explaining the pseudo-elements and techniques used.
Bonus challenge: Create a pure CSS image gallery with captions that appear on hover using ::before or ::after pseudo-elements.
Conclusion
Pseudo-elements and generated content are powerful CSS features that allow you to enhance your web pages without adding extra HTML elements. They enable you to:
- Create sophisticated typographic treatments
- Add decorative elements and visual indicators
- Generate dynamic content based on element attributes
- Build interactive UI components with minimal markup
- Implement automatic numbering and complex styling patterns