On this page
CSS Combinators: Descendant, Child, Adjacent & General Sibling
CSS Combinators: Selecting Elements by Relationship
CSS combinators let you select elements based on their relationship in the HTML tree: parent, child, ancestor, descendant and sibling. Instead of adding classes to every element, combinators let you style structure precisely. There are four main combinators: the descendant (space), the child (>), the adjacent sibling (+) and the general sibling (~).
Understanding the DOM Family Tree
- Parent - an element directly containing another.
- Child - an element directly inside a parent.
- Ancestor - a parent, grandparent and so on, up the tree.
- Descendant - a child, grandchild and so on, down the tree.
- Siblings - elements that share the same parent.
1. Descendant Combinator (Space)
Syntax: ancestor descendant. It matches the second element anywhere inside the first, no matter how deeply nested.
nav amatches every link inside a nav, even links inside nested lists.- It is the most common combinator, but also the easiest to overuse because it can match more than you intend.
2. Child Combinator (>)
Syntax: parent > child. It matches only direct children, not grandchildren.
ul > liselects only the list items of that list, not the items in nested lists.- Use it for precision and to avoid unwanted inheritance in nested components such as menus.
3. Adjacent Sibling Combinator (+)
Syntax: first + second. It matches the second element only if it comes immediately after the first, and both share the same parent.
h2 + pselects the first paragraph directly after each h2.- Classic use: spacing rules, such as
.item + .item { margin-top: 1rem; }(a lightweight alternative to adding margins to the last child).
4. General Sibling Combinator (~)
Syntax: first ~ second. It matches every following sibling of the first element, not only the next one.
h2 ~ pselects all paragraphs that follow an h2 within the same parent.- Famous use: the checkbox hack,
input:checked ~ .menu { display: block; }, which toggles UI with no JavaScript.
Important Limitation: CSS Looks Forward Only
Sibling combinators only select elements after the reference element in the source order. Traditionally CSS could not select a parent or previous sibling. The modern :has() pseudo-class now makes this possible, for example li:has(+ li.active) (see the pseudo-classes lesson).
Comparison Table
| Combinator | Symbol | Example | Matches |
|---|---|---|---|
| Descendant | space | div p | Any p inside div, at any depth |
| Child | > | div > p | p that is a direct child of div |
| Adjacent sibling | + | h2 + p | The p immediately after h2 |
| General sibling | ~ | h2 ~ p | All following p siblings of h2 |
Specificity of Combinators
Combinators themselves add no specificity. The score comes from the selectors they connect. So nav a is (0,0,2), while .nav > .link is (0,2,0). Long chains raise specificity and make styles harder to override, so keep chains short (2 to 3 parts is usually enough).
Combining with Other Selectors
.card > h3 + pselects the paragraph right after an h3 that is a direct child of .card..menu li:not(:last-child)adds a divider to every item except the last.form label + inputstyles the input right after a label.
Performance and Maintainability
Modern browsers match selectors right to left and handle combinators efficiently, so performance is rarely an issue. The bigger concern is maintainability: deeply nested selectors like body div.main ul li a span are brittle. If your HTML changes, the CSS breaks. Prefer a class on the element when the chain gets long. Native CSS nesting can also help you group related rules clearly.
Common Mistakes
- Confusing the descendant space with a compound selector (
.a .bis not.a.b). - Expecting
+to work for elements that are not immediate siblings, such as when a wrapper element sits in between. - Expecting
~to select previous siblings. - Writing overly long selector chains with high specificity.
Key Takeaways
- Descendant (space) = any depth; child (>) = direct children only.
- Adjacent (+) = the very next sibling; general (~) = all following siblings.
- Combinators add no specificity themselves, but chains do accumulate it.
- Keep chains short and use classes for clarity.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Make the links that are DIRECT children of the nav orange, but leave the link inside the nested dropdown div unchanged.
Press Run to execute.
Show expected output
nav > a { color: orange; }This is a self-check — compare your result with the expected output above.
Add a green border to the input that comes immediately after a label. Then make every paragraph that follows the h3 (as siblings) gray.
Press Run to execute.
Show expected output
label + input { border: 2px solid green; } h3 ~ p { color: gray; }This is a self-check — compare your result with the expected output above.
Was this page helpful?