Syntax
A B { } /* descendant */
A > B { } /* direct child */
A + B { } /* adjacent sibling */
A ~ B { } /* general sibling */Examples
Descendant vs Child Combinator
The key difference between space and >.
.card p { color: gray; } /* ANY <p> nested anywhere inside .card, at any depth */
.card > p { color: gray; } /* ONLY <p> elements that are DIRECT children of .card */Sibling Combinators
Targeting elements based on their position relative to a sibling.
h2 + p { margin-top: 0; } /* only the <p> immediately following an <h2> */
input:checked ~ label { color: green; } /* every label after a checked input, at any distance */Parameters and values
- (space) (descendant combinator): Matches elements nested anywhere inside, at any depth
- > (child combinator): Matches only direct (one-level) children
- + (adjacent sibling): Matches only the immediately following sibling
- ~ (general sibling): Matches all following siblings, not just the next one
Best practices
- Use the child combinator (>) when you specifically want to avoid styling deeply nested elements that happen to share a selector
- Use the adjacent sibling combinator (+) for spacing adjustments, like removing top margin from an element right after a heading
- The ~ general sibling combinator combined with :checked is a classic CSS-only trick for building interactive components without JavaScript
- Avoid overly long combinator chains (like .a .b > .c + .d) - they become fragile and hard to maintain as markup changes
At a glance
- Purpose
- Presentation and layout
- File extension
- .css
- Runs in
- Web browsers
- Usually used with
- HTML and JavaScript
Specifications & further reading
Related CSS documentation
Basic Selectors
CSS selectors target elements to style. Type selectors match by tag name, class selectors (.name) match elements with that class, ID selectors (#name) match a single unique element, and the universal selector (*) matches everything. Classes are the most commonly used selector for reusable styling.Attribute Selectors
Attribute selectors target elements based on the presence or value of an HTML attribute, using square bracket syntax. They support several matching modes beyond exact equality, including starts-with, ends-with, and contains, making them powerful for targeting elements without needing extra classes.:has() - The Parent Selector
:has() matches an element if any of the selectors passed to it match something within it - effectively letting you style a parent based on its children or descendants, something CSS could never do natively before. It has broad, mainstream browser support as of 2026 and is safe to use in production.:is() and :where()
:is() and :where() both accept a list of selectors and match any of them, letting you write more compact selector groups. The key difference is specificity: :is() takes on the specificity of its most specific argument, while :where() always contributes zero specificity, making its rules trivially easy to override.
CSS selectors target elements to style. Type selectors match by tag name, class selectors (.name) match elements with that class, ID selectors (#name) match a single unique element, and the universal selector (*) matches everything. Classes are the most commonly used selector for reusable styling.Attribute Selectors
Attribute selectors target elements based on the presence or value of an HTML attribute, using square bracket syntax. They support several matching modes beyond exact equality, including starts-with, ends-with, and contains, making them powerful for targeting elements without needing extra classes.:has() - The Parent Selector
:has() matches an element if any of the selectors passed to it match something within it - effectively letting you style a parent based on its children or descendants, something CSS could never do natively before. It has broad, mainstream browser support as of 2026 and is safe to use in production.:is() and :where()
:is() and :where() both accept a list of selectors and match any of them, letting you write more compact selector groups. The key difference is specificity: :is() takes on the specificity of its most specific argument, while :where() always contributes zero specificity, making its rules trivially easy to override.