CSS Nesting used to only be possible in CSS preprocessors like Sass and Less. But guess what, it's now built in native CSS and all modern browsers will support it after August 29th. Let's see what's CSS Nesting and how we can use it.
What is CSS Nesting?
CSS Nesting is a new feature that allows you to nest selectors inside other selectors. Nesting helps by reducing repetition, reducing the file size, better file organization and easier refactoring. Let's see an example without nesting:
/* No nesting */
article.card {
border: 1px solid lightgrey;
border-radius: 6px;
overflow: hidden;
}
article.card header {
border-bottom: 1px solid lightgrey;
background: #f1f3f4;
padding: 16px;
}
This example can be written with nesting like this:
/* With nesting */
article.card {
border: 1px solid lightgrey;
border-radius: 6px;
overflow: hidden;
& header {
border-bottom: 1px solid lightgrey;
background: #f1f3f4;
padding: 16px;
}
}
Nesting examples
To get a good feel of how nesting works, let's see some more examples.
Multi-level nesting
article.card {
border: 1px solid lightgrey;
border-radius: 6px;
overflow: hidden;
& header {
border-bottom: 1px solid lightgrey;
background: #f1f3f4;
padding: 16px;
& h2 {
margin: 0;
}
}
}
In this example we can see that we can nest as many levels as we want. The first level is the article.card
selector, which is the root selector. The second level is the & header
selector, which is nested inside the root selector. The header styles will only be applied to header
elements that are children of article.card
elements.
Nesting without the &
symbol
article.card {
border: 1px solid lightgrey;
border-radius: 6px;
overflow: hidden;
.content {
border-bottom: 1px solid lightgrey;
}
}
We don't always need to add the &
symbol to indicate nesting. If we don't add it, CSS will add &
+ " " (space) in front of the nested selector. By explicitly placing the &
symbol, we can control where the parent selector should be placed.
Nesting pseudo-classes
article.card {
/* ... */
:is(.content, footer) {
padding: 16px;
}
}
It's also possible to nest pseudo-classes like the :is()
in this example. As we saw in the previous example, if we don't add the &
symbol CSS is going to add that for us + an empty space, so this example will target all children of
the article.card
element that are either .content
or footer
elements. The "compiled" selector will look like this:
article.card :is(.content, footer): {
/* ... */
}
Nesting pseudo-elements
article.card {
/* ... */
& footer a {
/* ... */
&:hover {
background: maroon;
&::after {
content: '>';
margin-left: 12px;
}
}
}
}
Just like the pseudo-classes, we can also nest pseudo-elements. In this example we're defining hover styles for the a
element that's a child of the footer
. We're also defining styles for the ::after
pseudo-element of the a
element while it's being hovered. Feel free to play around with the example above to see the result.
Nesting @media
queries
article.card {
/* ... */
@media (min-width: 768px) {
/* ... */
}
}
We can also nest @media
queries. This is a great way to keep the styles for a specific breakpoint in one place. This way we can easily find and update them if needed.
The &
symbol
As mentioned in the previous examples, the &
symbol is used to indicate where the parent selector should be placed. This gives us more control over the final selector, and if needed, we can also completely rearrange the whole context. Let's see some examples:
a {
&:hover {
/* a:hover */
}
:hover {
/* a :hover */
}
}
In this example we can see the different output when we use the &
symbol and when we don't. In cases where we want to define pseudo-classes for the parent selector, we'd need to use the &
symbol. Otherwise, CSS will add the parent selector in front of the pseudo-class.
Since the &
symbol is just a placeholder for the parent selector, we can also use it multiple times in the same selector, and flip the context completely:
header {
& h2 {
/* header h2 */
}
h2 & & & {
/* h2 header header header */
}
}
Understanding the parser
The nesting parser is what's responsible for parsing the nested selectors. The way that the parser identifies if the selector is defining a nested style is by checking whether the selector contains one of the symbols below:
& @ : . > ~ + # [ *
You might recognize some of these symbols. Some of them are used for selectors, pseudo-classes and pseudo-elements, combinators, attribute selectors etc...
If the parser finds a nested selector that doesn't start with any of these symbols, it will fail to parse it, which will result in incorrect styles.
Conclusion
Nesting is a great feature that can help us write more maintainable CSS. It can also help us write less code since we don't need to repeat the parent selector over and over again. This is huge for CSS, and I'm excited to see all of the creative ways developers are going to come up with to use it.
Top comments (1)
Nice! Love that on native css! What’s that mean for preprocessors future like sass or less? 🙈