Combinators

Difficulty: Beginner

CSS combinators define relationships between selectors, allowing you to target elements based on their position in the HTML document tree. While basic selectors target elements by their name, class, or ID, combinators let you target elements based on their relationship to other elements - children, descendants, siblings, and adjacent elements. Mastering combinators lets you write precise selectors without adding extra classes to your HTML.

The descendant combinator (space) is the most commonly used combinator. It selects elements that are descendants of another element at any depth. Writing .sidebar a selects all <a> elements that are anywhere inside an element with class 'sidebar,' whether they are direct children, grandchildren, or deeper. This is powerful but can be too broad - it matches every link at every nesting level, which can cause unintended styling.

The child combinator (>) selects only direct children, not deeper descendants. Writing .nav > li targets only the <li> elements that are immediate children of .nav, not <li> elements nested in sub-menus. This is more precise than the descendant combinator and is useful when you need to style only the first level of a nested structure, like top-level navigation items in a multi-level menu.

The adjacent sibling combinator (+) selects an element that is the very next sibling of another element - both must share the same parent and the second must immediately follow the first. Writing h2 + p selects the first <p> that immediately follows an <h2>. This is useful for styling the introductory paragraph after a heading differently, or adding spacing after specific elements.

The general sibling combinator (~) selects all sibling elements that come after the first selector, not just the immediately adjacent one. Writing h2 ~ p selects all <p> elements that are siblings of an <h2> and come after it in the source order. Note that it only selects following siblings, not preceding ones. This combinator is useful for styling all content that follows a specific element, like showing elements after a checkbox is checked (using the :checked pseudo-class with ~).

Code examples

Descendant vs Child Combinator

/* Descendant combinator (space): any depth */
.nav a {
  color: white;
  text-decoration: none;
}
/* Matches: <nav class="nav"><a>, <nav class="nav"><div><a>, etc. */

/* Child combinator (>): direct children only */
.nav > li {
  display: inline-block;
  padding: 8px 16px;
}
/* Only matches <ul class="nav"><li>, NOT nested <li> inside sub-menus */

/* Practical example: multi-level menu */
.menu > li {
  /* Top-level items: horizontal */
  display: inline-block;
  position: relative;
}

.menu > li > a {
  /* Top-level links only */
  font-weight: 700;
  padding: 12px 16px;
}

.menu li li a {
  /* Nested (dropdown) links: different style */
  font-weight: 400;
  padding: 8px 12px;
  font-size: 14px;
}

The descendant combinator (space) matches at any nesting level. The child combinator (>) matches only direct children. Use > when you need precision, especially in nested structures like menus.

Adjacent and General Sibling Combinators

/* Adjacent sibling (+): immediately next sibling */
h2 + p {
  font-size: 1.125rem;
  color: #4b5563;
  margin-top: 4px;
}
/* Styles only the FIRST paragraph right after each h2 */

/* General sibling (~): all following siblings */
h2 ~ p {
  margin-left: 16px;
}
/* Styles ALL paragraphs that come after h2 (at same level) */

/* Practical: spacing between stacked elements */
.stack > * + * {
  margin-top: 16px;
}
/* Every direct child except the first gets a top margin */
/* This creates even spacing without margin on the first element */

/* Practical: style after a divider */
hr + p {
  font-style: italic;
  color: #6b7280;
}

The + combinator selects the single element immediately after. The ~ combinator selects all following siblings. The 'lobotomized owl' pattern (.stack > * + *) is a popular spacing technique.

Combining Combinators

/* Multiple combinators in one selector */

/* Direct child <a> inside any <li> inside .nav */
.nav li > a {
  padding: 8px 12px;
  display: block;
}

/* First paragraph after h2 inside .article */
.article h2 + p {
  font-weight: 500;
  font-size: 1.1rem;
}

/* Links inside list items that are direct children of .sidebar */
.sidebar > ul > li > a {
  color: #1e40af;
  border-bottom: 1px solid #e5e7eb;
  padding: 8px 0;
  display: block;
}

/* Table rows: alternate styling after header */
thead + tbody tr:nth-child(odd) {
  background-color: #f9fafb;
}

Combinators can be chained to create very specific selectors. .sidebar > ul > li > a traces a precise path through the DOM. Use these when you need surgical precision without adding extra classes.

Key points

Concepts covered

Descendant Combinator, Child Combinator, Adjacent Sibling, General Sibling, Nesting Selectors