CSS Pseudo-classes and Pseudo-elements: The Useful Ones, Explained

Pseudo-classes target states (:hover, :focus). Pseudo-elements create virtual elements (::before, ::after). One colon vs two โ that's the easy rule.
Pseudo-classes you'll use every day
.btn:hover { background: #2563eb; }
.btn:focus-visible { outline: 2px solid; outline-offset: 2px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.input:invalid { border-color: red; }
.row:nth-child(even) { background: #f8fafc; }Why :focus-visible beats :focus
:focus shows the outline on every click, which looks noisy. :focus-visible only shows it for keyboard focus โ the accessibility win you actually want.
Structural pseudo-classes
- :first-child / :last-child
- :nth-child(n) โ zebra rows
- :not(selector) โ everything except
- :empty โ elements with no children
The game-changer: :has()
/* Style a card differently when it contains an image */
.card:has(img) { padding-top: 0; }
/* Form label turns red when the input is invalid */
label:has(+ input:invalid) { color: red; }Pseudo-elements: ::before and ::after
.required::after {
content: " *";
color: red;
}They need a content property to render โ even an empty string works. Use them for decorative icons, custom underlines, or quote marks.
Other pseudo-elements worth knowing
- ::placeholder โ style input placeholders
- ::selection โ style highlighted text
- ::marker โ style list bullets
- ::file-selector-button โ style file input button
Frequently asked questions
What's the difference between : and ::?
Single colon = pseudo-class (state). Double colon = pseudo-element (virtual element). Modern CSS requires :: for pseudo-elements.
Does :has() hurt performance?
Modern engines optimise it well. Don't preoptimise โ use it where it makes your CSS simpler.
External references
Enjoyed this article?
Share it with a fellow developer or explore more tutorials in our blog.
More articles