Pseudo-Elements: Decorative Shapes and Sub-Element Styling

Master CSS Pseudo-Elements. Learn how to generate content using ::before and ::after, style first letters, and customize selection highlights.

Introduction

If you want to add a small arrow symbol ($\rightarrow$) next to every link on your page, you could manually type the character inside every HTML tag. However, if you ever want to change that arrow to a chevron, you have to modify dozens of pages.

In CSS, Pseudo-Elements solve this by letting you create decorative styles and insert content programmatically without altering the HTML structure. Marked by a double colon (::), pseudo-elements target specific parts of an element, such as its first letter, its first line, or generated slots placed before or after its content.


What You Will Learn

  • The difference between Pseudo-Classes (:) and Pseudo-Elements (::).
  • How to generate content using ::before and ::after.
  • Formatting drop caps using ::first-letter and ::first-line.
  • Customizing browser highlight colors using ::selection.

Prerequisites


Detailed Explanation: Sub-Element Selectors

A pseudo-element is appended to a selector using double colons: selector::pseudo-element.

  • Note: While older browsers support a single colon (e.g. :before), the W3C introduced the double-colon syntax (::before) in CSS3 to clearly distinguish pseudo-elements from pseudo-classes.

The Power of ::before and ::after

These two pseudo-elements inject virtual child elements before or after the targeted element's actual content.

  • The Content Property: For ::before and ::after to render at all, you must declare the content property, even if it is just an empty string (content: "";).
  • These are widely used to add decorative icons, underline animations, and background shape overlays without polluting the HTML markup.

Key Pseudo-Elements

| Pseudo-Element | Targets | Use Case | | :--- | :--- | :--- | | ::before | A virtual child before the content. | Adding icons, bullet custom shapes. | | ::after | A virtual child after the content. | Adding arrow indicators, line wraps. | | ::first-letter| The first character of block text. | Designing a large drop cap. | | ::selection | Text highlighted by the user's cursor. | Customizing selection background color. |


Visual Learning Diagram (Mermaid)

graph LR
    subgraph HTML element rendering
    BE[::before pseudo-element] -->|Appends| C[Actual Text Content]
    C -->|Appends| AE[::after pseudo-element]
    end

Code Examples

Example 1: Animated Button Underline

Let's build an interactive menu link with a sliding underline animation using ::after:

/* Base link styling */
.nav-link {
  color: #f8fafc;
  text-decoration: none;
  position: relative; /* Anchor for pseudo-element positioning */
  padding-bottom: 5px;
}

/* Create the underline element using ::after */
.nav-link::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 0%; /* Initial width is zero */
  height: 2px;
  background-color: #38bdf8; /* Blue underline */
  transition: width 0.3s ease; /* Smooth hover transition */
}

/* Slide in the underline on hover */
.nav-link:hover::after {
  width: 100%;
}

Example 2: Drop Caps and Text Highlight

Style paragraphs like a classic magazine drop cap and customize highlight selections:

/* Style the first letter */
.blog-post::first-letter {
  font-size: 3rem;
  font-weight: bold;
  color: #3b82f6;
  float: left;
  margin-right: 8px;
  line-height: 1;
}

/* Customize text selection highlights */
::selection {
  background-color: rgba(56, 189, 248, 0.3); /* Transparent sky-blue */
  color: #ffffff;
}

Summary

Pseudo-elements target specific segments of elements using double-colon (::) notation. By utilizing ::before and ::after accompanied by the content property, developers generate structural design layers, and use selection highlight overrides to create premium user interfaces.


Related Articles

Next Tutorial

If multiple class, element, and pseudo-selectors target the same paragraph, how does the browser calculate which style wins? Let's check: Selector Specificity.