Pseudo-elements

Understand pseudo-elements as a way to style or generate a specific sub-part of an element, and learn the most common ones: ::before, ::after, ::first-letter, ::first-line, ::selection, and ::placeholder.

Pseudo-elements let you target a specific PART of an element — not the whole thing, and not a state it's in, but a conceptual sub-piece, like 'the first letter of this paragraph' or 'the text the user has currently selected'. Some pseudo-elements can even generate entirely new content that doesn't exist in your HTML at all. They're written with a double colon, like ::before, to visually distinguish them from pseudo-classes.

Pseudo-classes vs. pseudo-elements: the key distinction

This distinction trips up a lot of learners, so let's be precise: a pseudo-class (single colon, :hover) selects a REAL element that already exists in the DOM, based on some state or condition it's currently in. A pseudo-element (double colon, ::before) targets a 'fake' or virtual sub-part of an element's box — a piece of it that doesn't correspond to its own HTML tag.

Modern CSS specifies the double-colon syntax (::) for pseudo-elements specifically to separate them from pseudo-classes, though for a few pseudo-elements that predate this rule (like :before and :after), browsers still accept the old single-colon form for backward compatibility. Best practice today is to always use double colons for pseudo-elements.

::before and ::after

::before and ::after insert a generated, virtual element as the first or last child inside the selected element's box. They do not appear in the actual HTML/DOM — they're rendered purely by CSS.

Critically, ::before and ::after do nothing visible unless you set the content property, even if that value is an empty string (content: "";). content is what tells the browser what (if anything) to render inside the generated box. Without it, the pseudo-element technically exists but has no box to display.

::first-letter and ::first-line

::first-letter targets just the first letter of a block-level element's text content — classically used for decorative 'drop caps' in article text. ::first-line targets the first rendered line of text, which is dynamic — it depends on the element's width, since the browser recalculates what counts as 'the first line' whenever the layout reflows (e.g. on window resize).

::selection

::selection lets you style the portion of text the user has currently highlighted/selected with their mouse or keyboard — for example, giving it a custom background color instead of the browser's default blue highlight. Only a limited set of properties can be styled here (mainly color and background-color, plus a few others).

::placeholder

::placeholder targets the placeholder text shown inside an empty <input> or <textarea> (the text set via the placeholder attribute) before the user types anything — commonly used to lighten or italicize placeholder text so it's visually distinct from real user input.

Pseudo-element syntax

Pseudo-elements use a double colon (::) in modern CSS. ::before and ::after require the content property to render anything.

selector::before {
    content: "";
}

selector::after {
    content: "";
}

selector::first-letter { }
selector::first-line { }
selector::selection { }
input::placeholder { }

Code Examples

Appends a red asterisk after any element with class 'required', without needing to hardcode the asterisk in the HTML itself — useful for marking required form labels.

Generates decorative opening and closing quotation marks around a <blockquote>, driven entirely by CSS. The quotes property defines which characters open-quote/close-quote actually insert.

Creates a classic magazine-style 'drop cap' effect — the first letter of each article paragraph is enlarged, floated, and colored differently from the rest of the text.

Customizes the browser's default text-selection highlight color across the whole page, and styles placeholder text in inputs to look visually distinct (gray, italic) from real typed content.

.required::after {
    content: " *";
    color: red;
}
blockquote::before {
    content: open-quote;
    font-size: 2em;
    color: #999;
}
blockquote::after {
    content: close-quote;
}
blockquote {
    quotes: "\201C" "\201D";
}
article p::first-letter {
    font-size: 3em;
    float: left;
    line-height: 1;
    padding-right: 6px;
    color: firebrick;
}
::selection {
    background-color: gold;
    color: black;
}

input::placeholder {
    color: #999;
    font-style: italic;
}

Pseudo-class vs. pseudo-element

State vs. sub-part is the core mental model: pseudo-classes ask 'what condition is this real element in?'; pseudo-elements ask 'which piece of this element's rendered box am I targeting?'

Pseudo-CLASS   (single colon)   -> a STATE of a real element
                                    e.g. button:hover

Pseudo-ELEMENT (double colon)   -> a virtual PART of an element's box
                                    e.g. p::first-letter, li::before

Important Rules

Pseudo-elements use double colons (::) in modern CSS syntax.::before and::after generate virtual content and require the content property to appear at all.::first-letter and::first-line only work on block-level (or similarly displayed) elements' text.::selection has a restricted set of stylable properties.::placeholder targets an input/textarea's placeholder attribute text, not its actual value.

Where you'll see this in real projects

::before and::after are workhorses for icon fonts, decorative shapes (like little triangle 'tooltip arrows'), and generated counters/labels without adding extra markup.::placeholder is used constantly to style form UX, and::first-letter is common in editorial/blog-style layouts for drop caps..badge::after { color: white; background: red; }.badge::after { content: "New"; color: white; background: red; }

Without the content property, ::before and ::after generate no visible box at all — the color and background declarations have nothing to render onto.

[Choose the Correct Syntax] Which of these correctly targets the placeholder text of an input field using modern CSS pseudo-element syntax? A. input:placeholder { color: gray; } B. input::placeholder { color: gray; } C. input[placeholder] { color: gray; } D. input::before { content: attr(placeholder); color: gray; }

B. input::placeholder { color: gray; } — ::placeholder is the correct pseudo-element (double colon) for styling placeholder text. A single colon (:placeholder) is not valid modern pseudo-element syntax, [placeholder] would select the input itself (any input that HAS a placeholder attribute) rather than the placeholder text's appearance, and ::before would generate new content rather than style the existing placeholder.

Write CSS that adds a small right-pointing arrow ('→') after every link inside an element with class 'external-links', using a pseudo-element..external-links a::after { content: " \2192"; }

Use ::after with the content property, targeting .external-links a.