Using selectors inside of selectors
Just as you nest <li>
s within an <ul>
in HTML, you can nest their selectors within Sass:
ul {
list-style: none;
text-align: right;
li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
}
}
Now that you have a clear hierarchy of the selectors reflecting their HTML structure, your <li>
s are nested within the <ul>
, just like they would be in HTML.
Rather than having to scroll through a CSS file looking for individual selectors and their rule sets, you can go to a single spot in the file and see how <ul>
s and their child <li>
are styled. That means you only have to go to one location to make updates in the future.
Cleaner? Check. ✅ Maintainable? Check. ✅
Take a look at the resulting CSS:
ul {
list-style: none;
text-align: right;
}
ul li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
}
The <ul>
received all of the rules assigned outside of the nested selector. And the <li>
received the rules that were assigned within its nested selector. Notice that the <li>
isn’t a singular element selector. Instead, we’ve created a descendant combinator.
Try it out for yourself!
Let’s give our CSS some structure and put our new-found nesting knowledge to use. In this interactive exercise, we have a .parent
selector, and a .child
selector. Let’s organize them to reflect their relationship using Sass’ nesting by placing the .child
rule set within the .parent
rule set.
Nest the .child
selector and its contents within .parent
Once you've tried it yourself, check your work in this CodePen!
We’ve performed the most basic form of nesting within Sass, which is placing a selector within another selector. Sass will always compile this as selectors separated by spaces, which, in CSS, is a descendant combinator.
Other types of combinators
If you can create descendant combinators, it stands to reason that you can create other combinators as well! In fact, you can create any of the other CSS combinators:
.parent {
background-color: #15DEA5;
}
.parent .descendant {
color: #fff;
}
.parent > .child {
color: #D6FFF5;
}
.parent + .adjacent {
color: #001534;
}
You remember these, right? Let's do a little recap:
In the first case, anything related to the parent element will take on the designated background color.
In the second case, if the second element is the descendant of the first element, then it will have the specified color.
In the third case, if the second element is a child of the first element, it'll have the designated color.
In the fourth case, if the second element is immediately preceded by the first, it'll have the supplied color.
Wait, what's the difference between "descendant" and "child"?
It's actually the same as in plain English:
You are both the child and the descendant of your parents.
You aren't the child of your grandparents, but you are their descendant.
A child element, therefore, has a direct and immediate link to the parent element.
By adding the appropriate combinator symbol before a nested selector, you can create any of the CSS combinators in Sass:
.parent {
background-color: #15DEA5;
.descendant {
color: #fff;
}
>.child {
color: #D6FFF5;
}
+.adjacent {
color: #001534;
}
}
When you nest a block in Sass, it creates an individual CSS selector with the parent selector appended, separated by the appropriate combinator.
Try it out for yourself!
It’s time to get all familial and whatnot as we create selectors that style based on their relationship to the root selector. The code base already contains a selector for the “root” class. Let’s use nesting and combinators to style the elements surrounding and within the .root
element in the HTML file.
Nest a selector for the
.general
class using the general sibling combinator (~
) and assign it a background color of your choosingNest a selector for the
.adjacent
class using the adjacent sibling combinator (+
) and assign it a background color of your choosingNest a selector for the
.child
class using the child combinator (>
) and assign it a background color of your choosing.Nest a selector for the
.descendant
class using the descendant.Normally the descendant combinator is a space “”, but with Sass, directly nesting a selector will compile as the space of the descendant combinator in the CSS.
Assign the nested
.descendant
selector a background color of your choosing.
Review the resulting rendered HTML in the browser pane and compare your answer to mine with this CodePen.
But what if we don’t want a separation between the parent and child?
To do that, you'll harness the power of the ampersand!
Enter the ampersand
You need to add a li:hover
pseudo-class to your <li>
s to provide visual feedback and better the user experience. So, let’s nest the pseudo-selector within our li
selector:
ul {
list-style: none;
text-align: right;
li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
:hover {
color: #001534;
}
}
}
Which compiles to:
ul {
list-style: none;
text-align: right;
}
ul li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
}
ul li :hover {
color: #001534;
}
Well, that won't work… 😕 Remember that nesting in Sass creates combinators. But you don’t want a combinator before your pseudo-selector. That’s not how they work; you need it to be immediately joined to their selector to have the desired effect.
Sass has a special character to concatenate the parent and child selectors: the ampersand (&)! Prefixing a nested selector with an ampersand will directly join it to the parent selector without any combinators.
So, let’s add an ampersand before our :hover
pseudo-selector:
ul {
list-style: none;
text-align: right;
li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
&:hover {
color: #001534;
}
}
}
And check out the resulting CSS:
ul {
list-style: none;
text-align: right;
}
ul li {
display: inline;
font-size: 3rem;
color: #D6FFF5;
}
ul li:hover {
color: #001534;
}
Much better! Now we have a properly functioning hover state for our <li>
s, all built within a single block of code.
Try it out for yourself!
Nesting in Sass is awesome, but sometimes we might want to nest a selector that doesn’t work any of the CSS combinators, such as pseudo-selectors like :hover
and :focus
. For times like those, we want to use the ampersand ( & ) to join the name of the parent to child being nested. In this interactive exercise:
Nest a
:focus
selector within the.input
class selectorAdd a new background color and font color to the the
:focus
selectorCheck out the result by interacting with the text box in the browser pane
Think you've got it? Compare your solution to mine in this CodePen!
Nesting: a dab'll do ya
There’s no physical limit as to how deeply you can nest within Sass. It can be tempting to completely mirror your HTML within your Sass, creating a perfect replication of the hierarchy.
.parent-div {
background-color: #15DEA5;
.child-div {
color: #fff;
.grandchild-div {
color: #D6FFF5;
}
}
}
But, remember, when you nest selectors, you increase the specificity of the compiled selectors. So, if you nest too deeply, you’ll end up with selectors of incredibly high specificity, making them extremely difficult to modify or override.
.parent-div {
background-color: #15DEA5;
}
.parent-div .child-div {
color: #fff;
}
.parent-div .child-div .grandchild-div {
color: #D6FFF5;
}
Trying to override a selector as specific as .parent-div .child-div .grandchild-div
would require an even more specific selector. It also greatly hinders the re-usability of your code; unless you were to add an element later that exactly matches this hierarchy of classes, this selector is completely useless. This is brittle code that will be difficult to maintain. And that defeats the purpose of using a preprocessor at all!
Rather than trying to replicate the hierarchy of the HTML, it is better to write nested selectors only relative to the root selector. It won’t represent the HTML structure as clearly, but it helps to ensure a low specificity in your codebase while maintaining flexibility and modularity.
Try it out for yourself!
Time to roll up your sleeves for another interactive exercise!
In this CodePen, move the nested .particle
selector up one level in the hierarchy, putting it on the same level as the .atom
selector.
The rendered HTML should now look a bit different, with another .particle
div showing up that wasn’t styled by the previously over-specific .particle
selector.
Check your end result with this CodePen.
Nesting in Sass is an extremely powerful tool that helps you to group relevant selectors, creating a codebase that is easy to navigate and maintain. However, it does have the drawback of ballooning specificity. Now, you might be thinking back to BEM, and the organization and structure it helps impart on your selectors. You might also recall that it’s built upon a foundation of low specificity. Which is at direct odds with the increase in specificity that comes from nesting. So, which should you use? BEM or nesting?
I would ask: why not have your cake and eat it too? 🍰 That's what we'll show you how to do in the next chapter. We’ll combine the power of Sass and the order of BEM to create flat selectors, while maintaining the structure of nesting.
Let's recap!
In Sass, selectors can be nested inside other selectors.
Sass can create any of the CSS combinators.
Use the ampersand to join a nested selector to a parent selector without using a combinator.
Be wary of creating overly specific selectors.