Nesting CSS style rules can make your stylesheets more organized, easier to read, and more maintainable.
Overview
Now that you've learned about selectors, you're probably wondering about a way to better organize them in your stylesheets. Imagine you were applying styles to items inside of a "feature" section on your site. With nesting, you can group these styles inside of the .feature
rule like this:
.feature {
button {
color: blue;
}
.link {
color: red;
}
.text {
font-size: 1.3em;
}
}
This would be the same as writing each style separately:
.feature button {
color: blue;
}
.feature .link {
color: red;
}
.feature .text {
font-size: 1.3em;
}
Nesting can go as many layers deep as needed.
.feature {
.heading {
color: blue;
a {
color: green;
}
}
}
Grouping and establishing relationships
Nesting lets you more succinctly group and establish relationships between style rules.
The nested rule will by default be related to the outer rule as a descendant combinator. Use selectors on the nested rules to change the relationships.
/* targets headings that are siblings of the .feature element and come immediately after it */
.feature {
+ .heading {
color: blue;
}
/* targets all paragraphs that are direct children of the .feature element */
> p {
font-size: 1.3em;
}
}
Define explicit relationships with the &
selector
You can also use the &
selector to be more explicit when nesting style rules. Think of &
as a symbol representing the parent selector.
.feature {
& button {
color: blue;
}
}
This would be equivalent to writing the styles like this:
.feature button {
color: blue;
}
When &
is required
Without &
, nested selectors will be descendent selectors of the parent selector. To form compound selectors, &
is required.
.feature {
&:last-child {
/* Selects the .feature element that is the :last-child, equivalent to .feature:last-child */
}
& :last-child {
/* Selects the :last-child inside of a .feature element, equivalent to .feature :last-child */
}
&.highlight {
/* Selects .feature elements that also have a .highlight class, equivalent to .feature.highlight */
}
& .highlight {
/* Selects elements inside of the .feature element with the class .highlight, equivalent to .feature .highlight */
}
}
You can also change the context and place the &
selector at the end of the child selector, or on both sides of it.
/* Targets buttons with an adjacent sibling button */
button {
& + & {
/* … */
}
}
img {
.my-component & {
/* styles for images inside of `.my-component` ... */
}
}
In the last example, we are adding styles for images inside of an element with the .my-component
class. This can be useful if you are working on a project where you can't add a class
or an id
to an element.
Nesting and specificity
Like :is()
, the nesting selector takes the specificity of the selector with the highest specificity in the parent's selector list.
#main-header,
.intro {
& a {
color: green;
}
}
.intro a {
color: blue;
}
The first rule targets all of the links inside of the #main-header
and .intro
elements, giving them a green color.
The second rule attempts to override this to make links inside of the .intro
element blue.
We can see why this doesn't work if we look at the specificity of each rule.
/* equivalent to :is(#main-header, .intro) a with a specificity of (1, 0, 1) */
#main-header,
.intro {
& a {
color: green;
}
}
/* lower specificity of (0, 1, 1) */
.intro a {
color: blue;
}
Since the first rule has an id
in its selector list, and nested rules take the specificity of the selector with the highest specificity, it has a higher specificity than the second rule. The links are green even for a
elements that are not inside an element with the #main-header
selector.
Invalid Nesting
Similar to :is()
, the nesting selector cannot represent pseudo elements.
blockquote, blockquote::before, blockquote::after {
color: navy;
& {
border: 1px solid navy;
}
}
You would expect the blockquote
and its pseudo-elements to both have navy
colored text and borders, but that's not the case. Since the &
selector can't represent pseudo-elements, the nested border styles will only apply to the blockquote.
When making compound selectors using &
and type selectors, the type selector must go first without any whitespace between.
/* valid css nesting */
.feature {
p& {
font-weight: bold;
}
}
/* invalid css nesting */
.feature {
&p {
font-weight: bold;
}
}
This rule allows CSS nesting to work alongside pre-processing tools like Sass. In Sass, writing &p
would append the parent selector to the nested type selector and the result would be .featurep
.
Nesting at-rules
CSS conditional group rules like @container
, @media
, @supports
, and @layer
can also be nested.
.feature {
@media (min-width: 40em) {
/* ... */
}
@container (inline-size > 900px) {
/* ... */
}
}
.feature {
@supports (display: grid) {
/* ... */
}
}
.feature {
@layer component {
h2 {
/* ... */
}
}
}
Check your understanding
When using CSS Nesting, what does the &
selector represent?
You can only nest two levels deep.
Which at-rules can be nested?
@media
@container
@import
@supports
@layer