Munsif.
AboutExperienceProjectsAchievementsBlogsContact
HomeAboutExperienceProjectsAchievementsBlogsContact
Munsif.

Frontend Developer crafting scalable web applications with modern technologies and clean code practices.

Quick Links

  • About
  • Experience
  • Projects
  • Achievements
  • Blogs
  • Contact

Connect

© 2026 Shaik Munsif. All rights reserved.

Built with Next.js & Tailwind

0%
Welcome back!Continue where you left off
Back to Blogs
CSS

CSS Pseudo-Elements Mastery: ::before, ::after, Counters & Decorative Techniques

Master CSS pseudo-elements for decorative, semantic design. Covers ::before/::after, the content property, decorative lines, CSS-only tooltips, drop caps, ::selection, ::marker, CSS counters, pure CSS icons, overlay effects, and best practices.

Apr 1, 202621 min read
CSSPseudo-ElementsDesign PatternsIconsMastery
CSS MasteryPart 10 of 10
  • 1. CSS Flexbox Mastery: The Complete Visual Guide to Flexible Layouts
  • 2. CSS Grid Mastery: From Basics to Advanced Layouts
  • 3. CSS Custom Properties (Variables) Mastery: Dynamic Theming & Beyond
  • 4. CSS Animations & Transitions Mastery: From Hover Effects to Keyframe Sequences
  • 5. CSS Selectors Deep Dive: From Basic to :has() & :is()
  • 6. CSS Box Model Mastery: Content, Padding, Border & Margin Explained
  • 7. CSS Responsive Design Mastery: Mobile-First, clamp(), & Container Queries
  • 8. CSS Specificity & Cascade Mastery: How Browsers Resolve Conflicts
  • 9. CSS Modern Layout Techniques: Scroll Snap, Subgrid, Logical Properties & More
  • 10. CSS Pseudo-Elements Mastery: ::before, ::after, Counters & Decorative Techniques

Pseudo-elements let you style parts of elements that don't exist in the HTML. They're virtual elements created entirely in CSS — perfect for decorative touches, icons, tooltips, and visual effects that keep your HTML clean and semantic.

Think of pseudo-elements as CSS's way of adding visual accessories without cluttering your markup. In this guide, we'll master every pseudo-element and build practical patterns you'll use in real projects.


1. What Are Pseudo-Elements?

Pseudo-elements target specific parts of an element's rendering. They use double colons (::) to distinguish them from pseudo-classes (:hover, :focus).

Pseudo-elementWhat It Targets
::beforeInserts generated content before element's content
::afterInserts generated content after element's content
::first-lineThe first rendered line of text
::first-letterThe first letter of the first line
::placeholderPlaceholder text in form inputs
::selectionText selected/highlighted by the user
::markerBullet/number of list items
::backdropBackground behind dialogs/fullscreen elements

Historical Note: Single colon (:before) works for backward compatibility, but double colon (::before) is the correct modern syntax.


2. ::before and ::after — The Power Duo

These are the most versatile pseudo-elements. They create child elements at the start or end of an element's content:

css
.label::before {
  content: "★ ";  /* Required! */
  color: #f59e0b;
}

.label::after {
  content: " →";
  color: #3b82f6;
}

The content Property Rules

::before and ::after require the content property. Without it, the pseudo-element doesn't exist.

ValueResult
content: ""Empty (used for decorative shapes)
content: "text"Inserts literal text
content: attr(data-label)Inserts the value of an HTML attribute
content: counter(name)Inserts a CSS counter value
content: url(icon.svg)Inserts an image
content: open-quoteInserts the opening quote character
Live Preview
Featured item with ::before star
Navigate to next page with ::after arrow
Using attr() to pull HTML data attributes

3. Decorative Lines and Dividers

One of the most common uses of ::before / ::after is creating decorative elements.

Heading with Line

css
.fancy-heading {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.fancy-heading::before,
.fancy-heading::after {
  content: "";
  flex: 1;
  height: 2px;
  background: linear-gradient(to right, transparent, #cbd5e1, transparent);
}

Accent Bar on Cards

css
.card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(to right, #3b82f6, #8b5cf6);
  border-radius: 4px 4px 0 0;
}
Live Preview
Section Title
Card with gradient accent bar
Created with ::before pseudo-element

4. Tooltips with Pseudo-Elements

Create CSS-only tooltips using ::before and ::after:

css
.tooltip {
  position: relative;
}

/* Tooltip text */
.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.5rem 0.75rem;
  background: #1e293b;
  color: white;
  font-size: 0.75rem;
  border-radius: 0.375rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}

/* Arrow */
.tooltip::before {
  content: "";
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: #1e293b;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}

.tooltip:hover::after,
.tooltip:hover::before {
  opacity: 1;
}
Live Preview
Hover the button to see the tooltip ↓

5. ::first-line and ::first-letter

Drop Cap Effect

css
.article p:first-of-type::first-letter {
  font-size: 3.5em;
  float: left;
  line-height: 0.8;
  padding-right: 0.1em;
  font-weight: 700;
  color: #3b82f6;
}

Styling the First Line

css
.article p::first-line {
  font-weight: 600;
  color: #1e293b;
  font-size: 1.1em;
}
Live Preview

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.


6. ::placeholder Styling

Customize the look of placeholder text in form inputs:

css
input::placeholder {
  color: #94a3b8;
  font-style: italic;
  font-size: 0.875rem;
}

/* Focused state — hide placeholder */
input:focus::placeholder {
  opacity: 0;
  transition: opacity 0.2s;
}
Live Preview
Click the input to see placeholder fade on focus

7. ::selection — Custom Text Highlight

Change how selected text looks:

css
::selection {
  background: #3b82f6;
  color: white;
}

/* Per-element selection */
.warning::selection {
  background: #fbbf24;
  color: #1e293b;
}
Live Preview
Try selecting this text to see the custom highlight ↓

Select this text to see a custom purple highlight! The ::selection pseudo-element lets you customize the background color and text color of user-selected text. This is a subtle but premium design touch.


8. ::marker — Custom List Bullets

Style the bullets or numbers of list items:

css
li::marker {
  color: #3b82f6;
  font-weight: 700;
  font-size: 1.2em;
}

/* Ordered lists */
ol li::marker {
  color: #8b5cf6;
  content: counter(list-item) ". ";
}
Live Preview
Colored markers
  • Styled bullet color
  • Bold and larger
  • Clean and simple
Emoji markers
  • Custom emoji bullets
  • Using content property
  • Simple and effective

9. CSS Counters with Pseudo-Elements

Create custom numbering systems without JavaScript:

css
.steps {
  counter-reset: step-counter;
}

.step {
  counter-increment: step-counter;
}

.step::before {
  content: "Step " counter(step-counter);
  display: inline-block;
  background: #3b82f6;
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
}
Live Preview
Initialize the project structure
Install the required dependencies
Configure the development server
Deploy to production

10. Shapes and Icons with Pseudo-Elements

Create pure CSS shapes without any images:

CSS Triangle

css
.arrow-down::after {
  content: "";
  display: inline-block;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 8px solid currentColor;
}

Hamburger Menu Icon

css
.hamburger {
  width: 24px;
  height: 2px;
  background: currentColor;
  position: relative;
}

.hamburger::before,
.hamburger::after {
  content: "";
  position: absolute;
  width: 100%;
  height: 2px;
  background: currentColor;
  left: 0;
}

.hamburger::before { top: -7px; }
.hamburger::after { top: 7px; }

Close (X) Icon

css
.close::before,
.close::after {
  content: "";
  position: absolute;
  width: 20px;
  height: 2px;
  background: currentColor;
  top: 50%;
  left: 50%;
}

.close::before { transform: translate(-50%, -50%) rotate(45deg); }
.close::after { transform: translate(-50%, -50%) rotate(-45deg); }

The CSS triangle trick works by making an element with zero width/height, then using thick borders. Since borders meet at 45° angles, making 3 sides transparent creates a triangle shape.

Live Preview
Triangle
Hamburger
Close (X)

11. Overlay and Gradient Effects

Dark Overlay on Images

css
.hero {
  position: relative;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0; /* top: 0; right: 0; bottom: 0; left: 0 */
  background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, 0.7));
  pointer-events: none; /* Allow clicks to pass through */
}

Gradient Border Effect

css
.gradient-border {
  position: relative;
  padding: 3px; /* The "border" width */
  background: linear-gradient(135deg, #3b82f6, #8b5cf6, #ec4899);
  border-radius: 0.5rem;
}

.gradient-border > * {
  background: white;
  border-radius: calc(0.5rem - 3px);
}
Live Preview
Dark Overlay
Gradient Border

12. Common Mistakes & Gotchas

Mistake 1: Forgetting content: ""

css
/* ❌ Won't appear */
.box::before {
  width: 50px;
  height: 50px;
  background: blue;
}

/* ✅ Works */
.box::before {
  content: ""; /* Required! */
  width: 50px;
  height: 50px;
  background: blue;
  display: block;
}

Mistake 2: Pseudo-Elements on Self-Closing Tags

::before and ::after don't work on elements that can't have children:

css
/* ❌ Won't work */
img::before { }
input::before { }
br::after { }

/* ✅ These work because they can have children */
div::before { }
span::after { }
a::before { }

Mistake 3: Content in ::before/::after for Screen Readers

Content added via content property is read by some screen readers. For purely decorative pseudo-elements, this can be confusing. Use aria-hidden on the parent or ensure the content doesn't convey important information.

Mistake 4: Not Using display: block on Empty Pseudo-Elements

Pseudo-elements are inline by default. If you're creating a decorative box:

css
/* ❌ Won't have dimensions */
.box::before {
  content: "";
  width: 100px;
  height: 100px;
}

/* ✅ display: block makes width/height work */
.box::before {
  content: "";
  display: block;
  width: 100px;
  height: 100px;
}

Conclusion

Pseudo-elements are CSS's secret weapon for clean, semantic design:

  • ::before / ::after — Add decorative content, shapes, overlays, tooltips
  • ::first-letter — Drop caps and typographic flair
  • ::first-line — Style the opening line of text
  • ::placeholder — Custom form input placeholders
  • ::selection — Custom text highlight colors
  • ::marker — Custom list bullets and numbering
  • CSS Counters — Automatic numbering without JavaScript
  • Pure CSS Icons — Triangles, hamburgers, close buttons

The golden rule: if something is purely decorative, it belongs in pseudo-elements. Keep your HTML for semantic content and let CSS handle the visual extras.

🧠 Test Your Knowledge

Now that you've learned the concepts, let's see if you can apply them! Take this quick quiz to test your understanding.

PreviousCSS Modern Layout Techniques: Scroll Snap, Subgrid, Logical Properties & More

Written by

Shaik Munsif

Read more articles

Found this helpful? Share it with your network!

On this page

0/27
Question 1 of 10Easy
Score: 0/0

What CSS property is REQUIRED for ::before and ::after to appear?