~/TechPurAI
~/tutorials/css-from-scratch/pseudo-classes-and-pseudo-elements
intermediate·part 17 of 22·3 min read

Pseudo-classes and pseudo-elements: hover, focus-visible, before, and after

Updated Aug 16, 2026CSS

:hover, :focus-visible, and ::before have all appeared in earlier parts without a full explanation. This part covers them properly — real pseudo-classes, which target an element in a particular state, and real pseudo-elements, which target a part of an element that isn't a genuine, separate node in the HTML at all.

Pseudo-classes: real, conditional states

css
a:hover { color: var(--color-accent); }
input:focus-visible { outline: 2px solid var(--color-accent); }
.product-card:first-child { margin-top: 0; }
li:nth-child(odd) { background: var(--color-bg); }
input:disabled { opacity: 0.5; }
input:checked + label { font-weight: 600; }

A pseudo-class (single colon) selects a real element based on a genuine state or position, rather than anything present in its actual markup — :hover for the real mouse-over state, :disabled for a real disabled form field, :nth-child() for structural position among siblings. None of these states could be targeted with the selectors covered in part 2 alone, since they're not attributes or classes — they're genuinely dynamic or structural conditions the browser tracks itself.

:hover vs. :focus vs. :focus-visible: a real, meaningful distinction

css
button:hover {
  background: var(--color-accent);
}

button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

:hover applies only to real mouse (or trackpad) interaction — genuinely meaningless for keyboard-only navigation. :focus applies whenever an element receives focus at all, by mouse click or keyboard tab. :focus-visible is a real, more recent refinement — it applies focus styling specifically when the browser determines focus was likely reached via keyboard, suppressing the outline for a plain mouse click, which is exactly why this series has used :focus-visible consistently since part 7 rather than plain :focus, avoiding an outline that some sighted mouse users find visually distracting while still guaranteeing it for real keyboard navigation.

:nth-child(): real, structural selection

css
.product-grid .product-card:nth-child(3n) {
  margin-right: 0;
}

tr:nth-child(even) {
  background: var(--color-bg);
}

:nth-child() accepts a real formula (odd, even, or an expression like 3n for every third element) to select elements by their real position among siblings — genuinely useful for the classic real "zebra-striped" table row pattern, or removing a right margin from every third card in a three-column row where gap (part 9) isn't being used.

Pseudo-elements: real, generated content that isn't a real DOM node

css
.field-error::before {
  content: "⚠ ";
}

.product-card::after {
  content: "";
  display: block;
  clear: both;
}

p::first-letter {
  font-size: 2em;
  font-weight: 700;
}

A pseudo-element (double colon, the real modern distinction from pseudo-classes) targets a genuinely generated part of an element — ::before and ::after insert real content immediately before or after an element's actual content, controlled entirely by the content property, without adding any real extra element to the HTML itself. ::first-letter and ::first-line target real, specific portions of text content directly, useful for a genuine drop-cap or lead-in styling effect.

Why it matters

::before and ::after are exactly how part 15's error-message warning icon and countless other real, small decorative additions get added without cluttering the real HTML with extra, semantically meaningless elements purely for styling hooks. The real rule of thumb: if the content is genuinely decorative (an icon, a visual flourish), ::before/::after is the right tool; if it's real, meaningful content a visitor needs, it belongs in the actual HTML, not generated purely through CSS where a screen reader may not reliably announce it.

Combining pseudo-classes and pseudo-elements

css
.product-card:hover::after {
  opacity: 1;
}

Pseudo-classes and pseudo-elements combine directly — this real rule targets the generated ::after content specifically when its parent .product-card is in a real hover state, a genuinely common pattern for revealing a decorative overlay or icon only on interaction.

Next: CSS and Core Web Vitals — render-blocking CSS, critical CSS, and preventing real layout shift, the direct performance connection between this series and the SEO series.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous16. Transitions and animations: real, tasteful micro-interactionsnext →18. CSS and Core Web Vitals: render-blocking CSS, critical CSS, and layout shift