Topic 19 of 30
Pseudo-Classes
Overview
Pseudo-classes select elements based on their state or position in the document tree. They eliminate the need for JavaScript to add/remove classes for hover, focus, active, first-child, and form validation states.
Syntax
css
/* User interaction */
a:hover { color: gold; }
button:active { transform: scale(0.97); }
input:focus { outline: 2px solid gold; }
/* Structural */
li:first-child { font-weight: bold; }
li:last-child { border: none; }
li:nth-child(2n) { background: #1a1a1a; } /* even rows */
li:nth-child(3n+1) { color: gold; }
/* Form states */
input:required { border-color: gold; }
input:valid { border-color: green; }
input:invalid { border-color: red; }
input:disabled { opacity: 0.5; }
input:checked + label { color: gold; }
/* Content */
p:empty { display: none; }
a:not(.active) { opacity: 0.7; }
/* Modern */
:is(h1, h2, h3) { line-height: 1.2; }
:where(section, article) p { margin-bottom: 1rem; }
.card:has(img) { padding: 0; }Common Pitfalls
- :nth-child() counts ALL siblings of the same parent — :nth-of-type() counts only same-type siblings.
- :is() and :where() both accept lists, but :where() has zero specificity — useful for reusable styles.
- Interview tip: :has() is the 'parent selector' CSS never had — .card:has(img) targets cards that CONTAIN an image.
Real-World Example
A complete form with validation pseudo-classes and interactive states:
example
css
/* Valid/invalid states for form fields */
.form-field input:not(:placeholder-shown):valid {
border-color: #10B981;
background: rgba(16, 185, 129, 0.05);
}
.form-field input:not(:placeholder-shown):invalid {
border-color: #EF4444;
background: rgba(239, 68, 68, 0.05);
}
/* Show error message only when invalid and not focused */
.form-field input:not(:placeholder-shown):invalid ~ .error-msg {
display: block;
}
/* Alternating table rows */
.data-table tbody tr:nth-child(even) {
background: rgba(255, 215, 0, 0.04);
}
.data-table tbody tr:hover {
background: rgba(255, 215, 0, 0.08);
cursor: pointer;
}
/* Zebra list */
.sidebar-nav a:first-child { border-top: 1px solid #333; }
.sidebar-nav a:last-child { border-bottom: none; }