Selectors help us target a specific HTML element we want to apply styling to on our page. There are many selectors available to us, allowing for very precise control over styling. The combinator is a type of selector that combines different selectors and gives them a useful relationship to each other.
Sometimes we want to style based on the relationship between two elements. On my blog, for example, paragraphs have different styling when it immediately follows a heading. This is exactly what combinators allow us to do.
In this post, we're going to take a closer look at each of the four combinators: descendent, child, general sibling, and adjacent sibling. We'll look at example code as well as visualizations of how the combinators work.
Descendent combinator
The descendent combinator — represented by a
(space) — will select all elements that are descendants of the first element. This is useful for when you want to change the styling of a component for a specific part of the page but not anywhere else on the page.
Example
An example of this would be div.content span
will match all <span>
elements inside the <div class='content'>
. Let's say we have the following CSS:
span {
background-color: red;
}
div.content span {
background-color: green;
}
The <span>
element has a red background in the above example. However, when a <span>
element is inside of a <div class='content'>
it will have a green background. To give you a better understanding of this, I've visualized it:
It doesn't matter if a <span>
element is a direct descendant of <div.content>
or not. As long as a parent, somewhere up the DOM tree, is <div.content>
it will have a green background color. This is not the case for our next combinator, though.
Child combinator
The child combinator — represented by a >
— will select all elements that are direct descendants, known as children, of the parent element. This is perfect for when you want to style, for example, a list within a list. Perhaps to-do items need to be bold and nested to-do items need to be a normal weight*.*
Example
Going back to our previous example, the descendant combinator, we can easily highlight the behavior by changing the combinator! So let's use the exact same code, but this time we're going to use the child combinator.
span {
background-color: red;
}
div.content > span {
background-color: green;
}
Just like before, the <span>
element has a red background. When a <span>
element is a child of a <div class='content'>
it will have a green background. However, if the <span>
element is nested deeper it will still be red. Let's visualize that:
As soon as the <span>
element is separated from its parent by being nested a level deeper, it will not be selected by the child combinator.
General sibling combinator
The general sibling combinator — represented by a ~
— will select all siblings that follow the first element. The sibling element doesn't have to directly follow the first element but they need to share the same parent element.
An example of this could be styling the paragraphs differently after using the first <h2>
element of an article. Usually, the content before that first <h2>
element is an introduction and you may want to have that in italics.
Example
Let's apply different styling to the <p>
elements after using the first <h2>
element.
p {
background-color: red;
}
h2 ~ p {
background-color: green;
}
Any <p>
element after <h2>
, as a sibling, will have a green background. All other <p>
elements will have a red background. Just like before, it's easier to see what's going on by visualizing it:
There's not much else to it. All <p>
elements after the <h2>
element, provided they share the same <article>
element as a parent, will have a green background. If a <p>
element is used before, or if it's nested, it will have a red background.
What if we just want to style the first <p>
element that follows the <h2>
element? Well, that's where our final combinator comes into play.
Adjacent sibling combinator
The adjacent sibling combinator — represented by a +
— will only select the direct sibling of the first element. This means that, unlike the general sibling combinator, the sibling needs to immediately follow the first element and share the same parent element.
A use case for this is when you want to change the margin-top
of the first <p>
element following an <h2>
element. On my blog, for example, the first paragraph following a heading has less margin at the top than the others. This allows the <h2>
element to sit more closely to the content it's the heading of.
Example
Using the previous example of <p>
elements following an <h2>
element, let's apply a different style to the <p>
that immediately follows an <h2>
.
p {
background-color: red;
}
h2 + p {
background-color: green;
}
All <p>
elements will be red, except for the ones that immediately follow an <h2>
. Again, to visualize this:
As you can see, the final <p>
element returns to the default red background.
The adjacent sibling combinator is probably my favorite of the four combinators outlined in this post. My blog converts my markdown written posts into HTML, which means all content elements are siblings of one another. The adjacent sibling combinator allows for fine control over styling in specific scenarios related to my content.
Combinators & CSS Specificity
Combinators have no impact on the specificity of your styling.
That's it... that's all there is to know about combinators in relation to CSS specificity. Not much of an exciting ending. I feel it's important to know that it doesn't, though. At first glance, using combinators appears to make your CSS more specific. While this is definitely true for you, the writer/reader of the code, this isn't true for calculating CSS specificity.
If you want to know more about CSS specificity and what does, or doesn't, have an impact, read this amazing post about CSS Specificity by Emma Bostian.
Final thoughts
Sometimes code snippets don't help me figure out what's going on. This is when I start visualizing the behavior of my code. Visualizing it helps me figure out how it works and hopefully it's been helpful to you too!
I hope you've enjoyed reading this post. Until next time 😊
Top comments (0)