Pseudo-Elements: ::before, ::after and ::marker

Pseudo-elements style a part of an element, or add decoration before or after it, such as a play icon, a quote mark, a list's numbers, a text box's placeholder or highlighted text, without changing the HTML.

Apprentice · CSS · Updated

After this lesson you can

  • Add decoration with ::before and ::after and the content property, keeping real information in the HTML
  • Style parts of an element with ::marker, ::placeholder and ::selection

Key takeaways

  • A pseudo-element is written with a double colon, as in li::marker, and styles a part of an element rather than the whole thing.
  • ::before and ::after add a box inside the element, before or after its content. They show nothing until you give them a content value, even an empty one: content: "".
  • Screen readers don't reliably read text added with content, and nobody can select or copy it. Keep real information in the HTML and use ::before and ::after for decoration.
  • ::marker styles a list's numbers or bullets, ::placeholder a text box's hint text, and ::selection the text someone highlights.

Look at the song that's playing in a music app: there's a little ▶ in front of it. A pull quote on a news site has a giant curly quote mark. A numbered recipe has big colored step numbers. Often, none of those are in the HTML. They're added by CSS, with pseudo-elements: parts of a page you can style that aren't elements of their own. You'll use them for small icons, decorations and list numbers, and to style parts of a page you can't reach any other way.

A double colon means "this part"

A pseudo-class (li:hover) picks an element in a certain state. A pseudo-element picks a part of an element, or adds a new part, and it's written with a double colon, ::, read "double colon": li::marker is read "li double colon marker", the number in front of a list item.

Here's the song that's playing now on the Road Trip Mix page, the playlist this chapter styles:

<li aria-current="true">
  <strong>Mile Marker 88</strong>
  <span>Coyote Radio</span>
  <span>4:05</span>
  <input type="checkbox" aria-label="Like Mile Marker 88">
</li>

aria-current="true" is the page's way of saying "this is the current song", and screen readers announce it. Older stylesheets write a single colon, as in :before, and browsers still accept it for the oldest pseudo-elements. New code uses two, so a pseudo-element never looks like a pseudo-class.

::before and ::after: a box at either end

::before adds a box inside the element, before its first piece of content, and ::after adds one after its last. They stay empty and invisible until you give them a content value: text in quotes, or "", an empty string, when the box is only a shape.

li[aria-current="true"] strong::before {
  content: "▶ ";
  color: #e63973;
}

Read it from the left: in the song whose aria-current is true, inside its <strong>, add a pink ▶ and a space before the title. The ▶ is a character like any letter, so it takes a color and a font size. Without the content line, nothing appears, and that's the first thing to check when a ::before doesn't show. One more catch: an <img> is replaced by its picture and holds no content of its own, so it gets no ::before or ::after. Form fields such as <input> get one in some browsers and not in others, so don't count on it there either.

Decoration only

Here's the rule: if it matters, put it in the HTML. Screen readers don't reliably read text added with content: some read it, some skip it, and none can tell it apart from the real text. Nobody can select it or copy it either, and search engines may ignore it.

  • Fine in ::before or ::after: the ▶ in front of the playing song (aria-current already says it's current), a decorative quote mark, a small arrow after a link that leaves the site, a colored bar or a dot.
  • Belongs in the HTML: "Explicit", a price, a warning, a song's length: anything a person would miss if it weren't there.

That's why the explicit songs on the page carry a real <abbr title="Explicit lyrics">E</abbr> in the HTML, and the ▶ comes from CSS.

Quotes and arrows

A friend left a note at the top of the mix. A big, decorative quote mark goes before it, and links that leave the site get a small arrow after them:

blockquote::before {
  content: "“";
  font-size: 3rem;
  color: #94a3b8;
}

a[href^="https://"]::after {
  content: " ↗";
}

The first rule puts a large gray curly quote before the <blockquote>, the element HTML uses for a quotation. The second adds ↗ after every link whose address starts with https://, the attribute selector from Attribute Selectors. Pseudo-elements go at the very end of a selector: a[href^="https://"]::after works, and a::after[href] doesn't.

Try it. In your road-trip.css, add the ▶ rule and the ↗ rule, and add a <blockquote> with a line of your own to road-trip.html. Then try to select the ▶ with your mouse: you can't, because it isn't really text on the page.

::marker, ::placeholder and ::selection

Three more pseudo-elements style parts the browser draws itself:

  • ::marker is a list item's number or bullet. The playlist is an <ol>, so its markers are the track numbers.
  • ::placeholder is the gray hint text in an empty text box, such as Search this mix.
  • ::selection is text the reader has highlighted by dragging over it.
li::marker {
  color: #e63973;
  font-weight: bold;
}

input::placeholder {
  color: #64748b;
}

::selection {
  background-color: #fde68a;
}

The track numbers turn pink and bold, the placeholder becomes a readable gray, and highlighted text turns yellow anywhere on the page. As of October 2026, Safari styles only a marker's color and font-size (the bold here won't show there), and Safari on iPhone and iPad ignores ::selection. Both are extras that the page doesn't depend on, so the page still works. Keep a placeholder dark enough to read, and never use it instead of a label: it disappears as soon as someone types.

The playlist's now-playing song with a pink play triangle before its title, pink bold track numbers down the left, and a highlighted line of text in yellow

Check yourself

2 questions to try on the lesson page.

So what

Pseudo-elements add the polish (an icon, a quote mark, styled numbers) without cluttering the HTML. Use them for decoration, keep the words that matter in the page itself, and everyone gets the same information.

Key terms

Cheat sheets

Related