CSS Pseudo-elements (::before, ::after, ::marker, ::selection, ::backdrop)

CSS pseudo-elements let you style parts of an element that do not exist as separate nodes in the HTML. They are useful for generated content, list markers, text selection styling, and certain modal or dialog backdrops.

Quick answer: Pseudo-elements target a part of an element, not the whole element itself. Use them when you want to add decorative or structural styling such as generated icons, custom list bullets, highlighted selections, or dialog backdrops.

Difficulty: Beginner

You'll understand this better if you know: basic CSS selectors, how the box model works, and the difference between HTML content and CSS styling.

1. What Are CSS Pseudo-elements?

CSS pseudo-elements are selector keywords that let you style a specific part of an element or insert content around it. They are written with a double colon, such as ::before, ::after, ::marker, ::selection, and ::backdrop.

Pseudo-elements are different from pseudo-classes. A pseudo-class selects an element in a particular state, while a pseudo-element selects part of an element or generated content.

2. Why CSS Pseudo-elements Matter

Pseudo-elements help you add visual details without changing your HTML structure. That keeps markup cleaner and can make styles easier to maintain.

They are especially useful when you need:

They also support better separation of concerns. If the content is truly meaningful, it belongs in HTML. If it is only visual, a pseudo-element is often the better choice.

3. Basic Syntax or Core Idea

Pseudo-elements are attached to a selector, just like other selector parts. The most common syntax uses the double-colon form.

Basic selector pattern

Here is the general pattern for pseudo-elements:

selector::pseudo-element {
  property: value;
}

The selector matches the element, and the pseudo-element styles the part you want to affect. Some pseudo-elements, such as ::before and ::after, usually need the content property to appear.

Minimal example

This example adds decorative text before a heading:

h2::before {
  content: "★ ";
  color: #c28a00;
}

In this case, the star is generated by CSS, not written into the HTML.

4. Step-by-Step Examples

Example 1: Adding decorative content with ::before

::before is often used for labels, icons, or visual emphasis. The content appears before the element's actual text.

.note::before {
  content: "Note: ";
  font-weight: bold;
}

This is useful when the label is purely visual and does not need to be part of the HTML content.

Example 2: Appending a symbol with ::after

::after places generated content after the element's text. It is common for external-link indicators, required markers, or small decorative separators.

a::after {
  content: " ↗";
}

The arrow is added visually after every link without changing the HTML.

Example 3: Styling list markers with ::marker

::marker targets the marker box of list items. This is the modern way to style bullets and numbers directly.

li::marker {
  color: #0a66c2;
  font-weight: bold;
}

This affects the bullet or number itself, not the whole list item.

Example 4: Styling text selection with ::selection

::selection applies when the user highlights text. It can improve readability and brand consistency when used carefully.

::selection {
  background: #1e90ff;
  color: #ffffff;
}

This styles the highlighted text across the page, not just one element.

Example 5: Styling dialog backdrops with ::backdrop

::backdrop is used with elements that create a top layer, especially dialog and fullscreen content. It styles the dimmed area behind the active element.

dialog::backdrop {
  background: rgba(0, 0, 0, 0.6);
}

This helps focus attention on the dialog by visually separating it from the page behind it.

5. Practical Use Cases

These use cases are strongest when the generated or target styling is presentational rather than content-critical.

6. Common Mistakes

Mistake 1: Forgetting the content property on ::before and ::after

::before and ::after do not render visible generated content unless content is set. Beginners often write styling but see nothing on the page.

Problem: The pseudo-element exists in the selector, but nothing appears because there is no generated content to render.

.badge::before {
  color: #fff;
  background: #d00;
}

Fix: Add a content value so the pseudo-element has something to display.

.badge::before {
  content: "New";
  color: #fff;
  background: #d00;
}

The corrected version works because the browser has generated content to paint.

Mistake 2: Using ::marker on the wrong element

::marker only applies to list-item markers. If you attach it to arbitrary elements, the style will not affect bullets or numbers.

Problem: The selector targets a paragraph, but paragraphs do not have list markers, so the rule has no visible effect.

p::marker {
  color: #0a66c2;
}

Fix: Use ::marker on list items or list-item-like elements.

li::marker {
  color: #0a66c2;
}

The corrected selector works because the target element actually generates a marker box.

Mistake 3: Expecting ::selection to style every detail of selected content

::selection can change the text and background of selected text, but it does not let you fully redesign the selection UI or style every property.

Problem: Trying to use unsupported properties creates confusion when the selection looks unchanged or partly styled only.

::selection {
  background: #222;
  padding: 12px;
  border: 2px solid #fff;
}

Fix: Limit ::selection to properties it supports well, such as background and color.

::selection {
  background: #222;
  color: #fff;
}

The corrected version works because it uses properties that browsers apply to highlighted text.

7. Best Practices

Practice 1: Use pseudo-elements for decorative content only

If the content matters to meaning, accessibility, or copying, put it in HTML instead of CSS. Use pseudo-elements for decorative labels, separators, and icons.

.card::before {
  content: "";
  display: block;
  height: 4px;
  background: #0a66c2;
}

This keeps decorative styling in CSS while preserving meaningful HTML content.

Practice 2: Prefer ::marker for list markers instead of removing bullets

Instead of stripping list semantics and rebuilding them with extra elements, style the marker directly when possible. That keeps lists accessible and simpler.

ul {
  padding-left: 1.5rem;
}

li::marker {
  color: #0a66c2;
}

This approach preserves the list structure while still giving you control over the marker appearance.

Practice 3: Keep ::selection readable and subtle

Selection styling should improve contrast, not fight the browser or overwhelm the page. Use clear foreground and background colors with sufficient contrast.

::selection {
  background: #004aad;
  color: #ffffff;
}

A restrained selection style is easier to read and less distracting.

8. Limitations and Edge Cases

Browser support is generally good for the modern double-colon syntax, but some older code bases still use single-colon forms for legacy compatibility. The double-colon notation is preferred for clarity.

9. Practical Mini Project

Let's build a small announcement card that uses several pseudo-elements together. The card will have a decorative top bar, a small label, and a custom highlight color for selected text.

::selection {
  background: #1f6feb;
  color: #ffffff;
}

.announcement {
  position: relative;
  padding: 1rem;
  border: 1px solid #cfd7e6;
  border-radius: 0.5rem;
  background: #ffffff;
}

.announcement::before {
  content: "Announcement";
  display: inline-block;
  margin-bottom: 0.5rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  font-weight: bold;
  color: #0b2e59;
  background: #dbeafe;
  border-radius: 999px;
}

.announcement::after {
  content: "";
  display: block;
  height: 4px;
  margin-top: 0.75rem;
  background: linear-gradient(90deg, #1f6feb, #58a6ff);
}

.announcement ul {
  padding-left: 1.25rem;
}

.announcement li::marker {
  color: #1f6feb;
}

This example combines generated labels, decorative structure, and custom list markers in a way that keeps the HTML simple.

10. Key Points

11. Practice Exercise

Expected output: A card with a generated label above the title, blue list markers, and a custom selection highlight when text is selected.

Hint: Remember that ::before needs content, and ::marker only affects list items.

::selection {
  background: #2b6cb0;
  color: #ffffff;
}

.product-card {
  padding: 1rem;
  border: 1px solid #d7dde8;
  border-radius: 0.5rem;
}

.product-card::before {
  content: "Featured";
  display: inline-block;
  margin-bottom: 0.5rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
  font-weight: bold;
  color: #1f2937;
  background: #e0f2fe;
}

.product-card ul {
  padding-left: 1.25rem;
}

.product-card li::marker {
  color: #2b6cb0;
}

The solution uses three pseudo-element families together while keeping the markup clean and semantic.

12. Final Summary

CSS pseudo-elements let you style specific parts of an element or generate decorative content without adding extra HTML. The most common ones are ::before, ::after, ::marker, ::selection, and ::backdrop.

Use them when the effect is visual, not content-critical. That usually means labels, icons, list markers, text selection colors, and backdrops for dialogs or fullscreen content. Keep in mind that some pseudo-elements need special conditions, such as content for generated content or an active top-layer element for ::backdrop.

As you practice, focus on choosing the right pseudo-element for the job and preserving semantic HTML. A good next step is to learn how pseudo-classes like :hover and :focus work alongside pseudo-elements in real layouts.