By WebNest Studio Engineering Team
CSS Tutorial
CSS Pseudo-classes and Pseudo-elements
Pseudo-classes and pseudo-elements both let you style things that plain selectors can't reach directly — a particular state of an element, or a specific part of it that doesn't correspond to an actual HTML tag. They look similar in syntax but serve distinct purposes, and mixing them up is one of the most common small mistakes in CSS.
Pseudo-classes (single colon, e.g. :hover) target an element in a particular state or position, while pseudo-elements (double colon, e.g. ::before) target a specific sub-part of an element that isn't represented by its own tag in the HTML.
Why it matters
Pseudo-classes style an element according to its state or position: hovered, focused, the first child, every other row. Pseudo-elements style a part of an element or add decoration without extra markup. Together they remove the need for a great many helper classes and bits of JavaScript.
Common Pseudo-classes
Pseudo-classes describe a condition or state that an element is currently in.
- :hover — applies while the user's pointer is over the element.
- :focus — applies while the element has keyboard or click focus, essential for accessible forms and interactive elements.
- :nth-child(n) — matches an element based on its numeric position among its siblings, accepting a number, a keyword (odd, even), or a formula (2n+1); useful for zebra-striping table rows or grid items.
- :first-child / :last-child — match an element only if it is, respectively, the first or last child of its parent.
Common Pseudo-elements
Pseudo-elements target a portion of an element rather than the whole thing.
- ::before / ::after — insert generated content immediately before or after an element's actual content, most often used with the content property for decorative icons, quotes, or visual flourishes without extra HTML markup.
- ::first-line / ::first-letter — target just the first line or first letter of a text block, commonly used for drop caps or stylized intros.
- ::placeholder — styles the placeholder text of a form input.
The Single-Colon vs. Double-Colon Convention
Modern CSS uses a single colon for pseudo-classes (:hover, :focus, :nth-child) and a double colon for pseudo-elements (::before, ::after, ::first-line) to make the distinction explicit. For backward compatibility, browsers still accept a single colon for the older pseudo-elements (:before, :after) from CSS2, but the double-colon syntax is the current standard and should be preferred in new code.
Examples
Combining a pseudo-class with a pseudo-element for a decorative hover effect
.link {
position: relative;
text-decoration: none;
}
.link::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
width: 0;
height: 2px;
background: currentColor;
transition: width 0.25s ease;
}
.link:hover::after {
width: 100%;
}
An underline (generated purely with CSS, no extra markup) grows smoothly from 0 to full width beneath the link text whenever the user hovers over it.
Zebra-striping table rows with :nth-child
tr:nth-child(even) {
background-color: #f7f7f7;
}
tr:nth-child(odd) {
background-color: #ffffff;
}
Table rows alternate between light gray and white backgrounds automatically based on their position, with no need to manually add a class to every other row.
Common Mistakes
- Using a single colon for pseudo-elements (:before instead of ::before) in new code, which works due to legacy support but goes against the modern convention.
- Forgetting that ::before and ::after require a content property (even an empty string, content: "";) to render at all.
- Styling :hover on touch devices and assuming it behaves identically to a real mouse hover, when touch interactions handle hover states differently or not at all.
- Using :nth-child when the intent was actually to count only elements of a specific type (in which case :nth-of-type is usually the correct choice among mixed siblings).
Exercise
Make the button's background gold while it is hovered. Give the button a 3px solid orange outline when it is focused with the keyboard. Make the first letter of the paragraph twice as large and bold. Add an asterisk after the heading text using CSS only.
Expected result: a large first letter, a heading followed by an asterisk, and a button that reacts to hover and to keyboard focus.
/* The page contains: <main> with an <h1>, a <p> and a <button> */
/* TODO: button background on hover */
/* TODO: button outline on keyboard focus */
/* TODO: first letter of the paragraph */
/* TODO: an asterisk after the heading */
Show hints
:focus-visiblematches when the browser decides a focus indicator is needed, which includes keyboard focus.::afterneeds acontentproperty, such ascontent: " *", or nothing is rendered.
Show solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */
button:hover {
background: gold;
}
button:focus-visible {
outline: 3px solid orange;
}
p::first-letter {
font-size: 2em;
font-weight: bold;
}
h1::after {
content: " *";
}
Quick quiz
1. What is the difference between a pseudo-class and a pseudo-element?
- A. There is none
- B. Pseudo-classes only work on links
- C. A pseudo-element selects a state
- D. A pseudo-class selects an element in a particular state; a pseudo-element selects a part of an element
Show answer
D. Pseudo-classes use one colon, as in :hover; pseudo-elements use two, as in ::before.
2. What must a ::before or ::after rule contain to be displayed?
- A. A width
- B. A class
- C. A z-index
- D. The content property
Show answer
D. It can be an empty string, content: "", but it must be present.
3. Which selector matches every even-numbered table row?
- A. tr:nth-child(even)
- B. tr:even
- C. tr::second
- D. tr:nth-of-row(2)
Show answer
A. :nth-child also accepts odd and formulas such as 3n + 1.
Interview questions
What is the difference between :nth-child and :nth-of-type?
Show answer
p:nth-child(2) matches a p that is the second child of its parent, counting every sibling whatever its type; if the second child is not a p, nothing matches. p:nth-of-type(2) counts only the p siblings and matches the second of them. They give the same result only when all the siblings are of the same type.
What are ::before and ::after used for?
Show answer
They insert a generated box as the first or last child of an element, with content supplied by the content property. They are used for decoration that does not belong in the HTML: icons, quotation marks, badges, custom list markers, overlays and shapes. Because the content is not part of the document, important text must not be placed there; some screen readers read it and some do not.
Key Points to Remember
- Pseudo-classes (single colon) target a state or position of a real element, like :hover, :focus, and :nth-child.
- Pseudo-elements (double colon) target a sub-part of an element, like ::before, ::after, and ::first-letter.
- ::before and ::after require the content property to display and are commonly used for decorative, markup-free visuals.
- Prefer the double-colon syntax for pseudo-elements in modern CSS, even though single-colon still works for legacy ones.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering