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::beforeImportant 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.