⚡IntermediateAdvanced Selectors
CSS Combinator Selectors
Select elements by their DOM relationship using descendant, child (>), adjacent sibling (+), and general sibling (~) combinators.
Overview
CSS combinators define the **relationship** between two selectors. Instead of selecting all `p` elements, you can select only `p` elements that are direct children of a `.card`, or only the paragraph that immediately follows an `h2`.
The four combinators:
| Combinator | Syntax | Meaning |
|---|---|---|
| Descendant | `A B` | B anywhere inside A (any depth) |
| Child | `A > B` | B that is a **direct child** of A |
| Adjacent sibling | `A + B` | B immediately following A (same parent) |
| General sibling | `A ~ B` | All B elements following A (same parent) |
Combinators are composable you can chain them: `.card > header + p` selects a `p` that immediately follows a `header` which is a direct child of `.card`.
Key Points
- •Descendant (space): matches any nesting depth "ul a" matches links inside any ul, no matter how deeply nested
- •Child (>): only matches direct children "ul > li" won't select li inside a nested ul
- •Adjacent sibling (+): selects the one element immediately after "h2 + p" matches only the first p after h2
- •General sibling (~): selects all following siblings "h2 ~ p" matches all p elements after any h2
- •Combinators do not increase specificity only the selector parts themselves count
- •The checkbox hack uses input:checked + label to toggle visibility without JavaScript
Examples
Child combinator for direct list items only
/* Only direct <li> children ignores nested lists */
.nav > li {
display: inline-block;
padding: 8px 16px;
}
/* Descendant all links at any depth */
.nav a {
color: #374151;
text-decoration: none;
}.nav > li targets only the first-level li items, not those inside a dropdown.
Adjacent sibling for form labels after inputs
/* Float label technique: label follows input */
.field > input:focus + label,
.field > input:not(:placeholder-shown) + label {
transform: translateY(-24px) scale(0.85);
color: #6366f1;
}The HTML order must be <input> then <label> for + to work.
General sibling for "zebra striping" without nth-child
/* Every paragraph after an h2 in an article */
article h2 ~ p {
margin-left: 16px;
border-left: 3px solid #e5e7eb;
padding-left: 12px;
}
/* All siblings after a .divider */
.divider ~ * {
opacity: 0.5;
}Common Mistakes
- Using descendant space when you mean child >: "nav li" accidentally styles nested submenu items too
- Expecting + to skip hidden elements it matches the next element in the DOM, even if display: none
- Forgetting that siblings must share the same parent elements in different containers are never siblings
Note
The "owl selector" (* + *) adds margin-top to every element that follows another element a clean way to add vertical rhythm without margin on the first child: .stack > * + * { margin-top: 1rem; }
Exercises
Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.
Loading editor…
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4