Complete CSS Course Module 6: Intermediate: The Cascade and Advanced Selectors

CSS Pseudo-elements: ::before, ::after and ::placeholder

  • Beginner
  • Lesson 6.3
  • 4 min read
  • 26 of 77 in the course

In short

CSS pseudo-elements let you style a specific part of an element, like its first letter or its placeholder text, or add decorative content with ::before and ::after. What is the difference between a pseudo-class and a pseudo-element?

CSS pseudo-elements let you style a specific part of an element, like its first letter or its placeholder text, or add decorative content with ::before and ::after.

What is the difference between a pseudo-class and a pseudo-element?

A pseudo-class selects a whole element in a certain state, like :hover. A pseudo-element selects or creates a part of an element, like the first line of a paragraph or an extra box before its content.

Pseudo-elements use two colons: ::before, ::after, ::placeholder. You may see old code with one colon, like :before. Browsers still accept it for a few old pseudo-elements, but two colons is the modern, correct way.

Pseudo-elementWhat it selects
::beforeA new box inserted at the start of the element's content
::afterA new box inserted at the end of the element's content
::placeholderThe placeholder text in an input or textarea
::first-letterThe first letter of a block of text
::first-lineThe first line of a block of text
::selectionText the user has highlighted with the mouse
::markerThe bullet or number of a list item

How do ::before and ::after work?

::before and ::after create a small extra box inside the element: one before its content and one after it. They only appear if you give them the content property. The content can be text, an empty string "", or a value like attr().

Think of them as two free stickers every element gets. You can put a word, an icon or a coloured shape on each sticker.

<p class="note">Save your work before you close the editor.</p>
.note {
  background: #eff6ff;
  border-left: 4px solid #2563eb;
  padding: 12px 16px;
}

.note::before {
  content: "Note: ";
  font-weight: bold;
  color: #2563eb;
}

The paragraph shows "Note: Save your work before you close the editor." The word "Note:" is not in the HTML, so you never need to type it again. Every .note paragraph gets it automatically.

Common mistake: Forgetting content. Without it, ::before and ::after do not appear at all, even if you give them a size and background. For a purely decorative shape, use content: "";.

Note: Text added with content cannot be selected or copied, and some screen readers skip it. Only use it for decoration or small labels, never for important information.

Decorative icons and badges with ::after

Pseudo-elements are great for small visual extras that don't belong in the HTML. Here is a "NEW" badge and an arrow on a link:

<a class="course-link new" href="/css-grid">CSS Grid course</a>
.course-link {
  color: #1f2937;
  font-weight: 600;
  text-decoration: none;
}

.course-link::after {
  content: " →";
  display: inline-block;
  transition: transform 0.2s;
}

.course-link:hover::after {
  transform: translateX(4px);
}

.new::before {
  content: "NEW";
  background: #dc2626;
  color: white;
  font-size: 0.7rem;
  padding: 2px 6px;
  border-radius: 4px;
  margin-right: 8px;
  vertical-align: middle;
}

The link shows a small red "NEW" badge before the text and an arrow after it. When you hover the link, the arrow slides a little to the right. Notice the selector .course-link:hover::after: a pseudo-class comes first, then the pseudo-element.

You can also use content: attr(name) to show an attribute's value. For example, abbr::after { content: " (" attr(title) ")"; } prints the full meaning after an abbreviation.

Styling text parts: ::first-letter, ::first-line and ::selection

These pseudo-elements style part of the text without extra <span> tags:

.story::first-letter {
  float: left;
  font-size: 3.5rem;
  line-height: 1;
  font-weight: bold;
  color: #7c3aed;
  margin-right: 8px;
}

.story::first-line {
  font-variant: small-caps;
}

::selection {
  background: #fde68a;
  color: #1f2937;
}

The first letter of each .story paragraph becomes a big purple "drop cap", like in a printed magazine. The first line appears in small capital letters, and it adjusts by itself when the window is resized. When you highlight any text on the page, the highlight is soft yellow instead of the default blue.

Styling placeholders and list markers

The ::placeholder pseudo-element styles the hint text inside an empty input. The ::marker pseudo-element styles the bullets or numbers of list items.

<input class="search" type="search" placeholder="Search lessons...">

<ol class="steps">
  <li>Install a code editor</li>
  <li>Create an index.html file</li>
  <li>Open it in your browser</li>
</ol>
.search::placeholder {
  color: #9ca3af;
  font-style: italic;
}

.steps li::marker {
  color: #16a34a;
  font-weight: bold;
}

The placeholder text "Search lessons..." appears in light grey italics. The numbers 1, 2 and 3 in the list turn green and bold, while the list text stays normal. Before ::marker existed, you needed tricks with ::before to do this.

Tip: Keep placeholder text readable. Very light grey looks elegant but can be hard to read, and a placeholder is not a replacement for a <label>.

Practice exercise

Create a blog post title <h2 class="post-title"> and a quote <blockquote class="quote">. Use ::before to put a small coloured line above the title, and use ::before and ::after to add big curly quote marks around the quote. Finally, give the page a custom ::selection color.

Hint: for the line, use content: ""; with display: block, a width, a height and a background. For quote marks, use content: "“"; and content: "”";.

Key takeaways

  • Pseudo-elements use two colons and select or create a part of an element.
  • ::before and ::after need the content property to appear.
  • Use generated content for decoration, not for important information.
  • ::first-letter, ::first-line and ::selection style parts of text without extra HTML.
  • ::placeholder styles hint text in inputs, and ::marker styles list bullets and numbers.