CSS Combinators: Descendant, Child, and Sibling Selectors

Published Updated

A combinator is the symbol between two selectors that tells the browser how the elements should be related before the rule applies. For the broader styling basics, start with the CSS learning guide. This guide is part of our CSS Selectors series.

Syntax

.parent .descendant { /* space: any descendant, nested at any depth */ }
.parent > .child     { /* >: direct child only */ }
.item + .next        { /* +: the very next sibling */ }
.item ~ .later       { /* ~: any following sibling */ }

The Descendant Combinator

A plain space between two selectors matches the second element when it sits anywhere inside the first, no matter how deeply it is nested. This is the combinator you will use most, because it lets you scope styles to a region without touching the same elements elsewhere:

.article a {
  text-decoration: underline;
}

That rule underlines every link inside .article, whether the link sits directly in a paragraph or several levels down in a list. The reach is wide, so keep the left side specific enough that you do not accidentally style links in some unrelated corner of the page.

Because the descendant combinator does not care about depth, it also does not care about intervening elements. .article a matches a link tucked inside three nested divs just as readily as one sitting directly in the article, which is convenient until a component you did not expect gets nested inside .article and quietly inherits styles meant for something else.

The Child Combinator

The > symbol tightens things up by matching only direct children of the first element. Nesting no longer leaks your styles downward, which matters a lot for menus and nested lists:

.menu > li {
  border-bottom: 1px solid var(--color-border);
}

Here only the top-level list items get a border. If a .menu item contains its own sub-list, those inner items stay untouched, because they are grandchildren rather than direct children.

The child combinator only matches direct children. In .menu > li, a list item deeper inside the outer menu does not match through that outer menu. But if the nested list also has the menu class, its own direct children match too. Use a different class when the dropdown needs different styles.

The Sibling Combinators

The + combinator matches the element immediately after another at the same level, and the ~ combinator matches every following sibling. Both are handy for spacing and for reacting to a neighbor's state:

h2 + p {
  margin-top: 0;
}

.error ~ .field {
  opacity: 0.6;
}

The first rule removes the top margin from a paragraph that directly follows an h2, tightening the gap under a heading. The second dims every .field that comes after a .error element in the same parent.

Both sibling combinators only look forward and only within the same parent, so .item + .next cannot reach across into a different list even if the markup looks similar. The + version is the one you will reach for most, since spacing adjustments almost always care about exactly one neighbor rather than every element that follows.

Can CSS Select a Parent or Previous Sibling?

CSS can select a parent or an earlier sibling through :has(). For example, .card:has(img) matches a card containing an image, and h2:has(+ p) matches a heading immediately followed by a paragraph. Ordinary combinators still describe descendants or following siblings; :has() lets you test those relationships from the element you want to style.

Example

.recipe > h3 {
  margin-block: 1rem 0.25rem;
}

.recipe h3 + ul {
  margin-top: 0;
}
<section class="recipe">
  <h3>Ingredients</h3>
  <ul>
    <li>Flour</li>
    <li>Water</li>
  </ul>
</section>

The .recipe > h3 rule spaces the heading because it is a direct child of .recipe. The h3 + ul rule pulls the list up tight against that heading because the list is the immediate next sibling. Together they give you controlled spacing without adding a single utility class.

Frequently Asked Questions

Can CSS child and descendant combinators appear in one selector?

CSS child and descendant combinators can appear in the same selector. For example, .card > div a selects links at any depth inside a div that is a direct child of .card. Each combinator describes one relationship.

Is there a parent selector in CSS?

CSS :has() can select a parent based on a matching child or descendant. For example, .card:has(img) selects cards containing an image. It also accepts relative sibling selectors, so it is more flexible than a parent-only selector.

How many CSS combinators should you chain in one selector?

Chain only the CSS combinators needed to identify the intended element. Deep selectors become dependent on the current HTML structure and can stop matching when a wrapper changes. A class on the target element is often clearer.

Do CSS combinators work with pseudo-classes?

CSS combinators work with pseudo-classes. For example, input:checked + label selects the label immediately after a checked input with the same parent. The combinator describes the relationship, while the pseudo-class checks the state.

Do sibling combinators require both elements to share a parent?

CSS sibling combinators require both elements to have the same parent. The + combinator matches the next element sibling, while ~ can match later element siblings. Visual proximity alone does not make elements siblings.

Sources

  1. [1]
    Descendant combinator
    (developer.mozilla.org)
  2. [2]
    Child combinator
    (developer.mozilla.org)
  3. [3]
    Next-sibling combinator
    (developer.mozilla.org)
  4. [4]
    :has()
    (developer.mozilla.org)