Native CSS nesting: What you need to know - LogRocket Blog (2024)

Editor’s note: This article was updated by Oscar Jite-Orimiono on 27 December 2023 to provide the most recent information regarding native support for CSS nesting, along with updated examples and interactive CodePen demos.

Native CSS nesting: What you need to know - LogRocket Blog (1)

Native CSS nesting is when we nest a CSS style rule within another rule to write cleaner and more understandable code. This feature was previously completely unsupported on any browser without the help of a preprocessor like Sass or Less, but it now has partial or full support on many browsers.

In this tutorial, we will discuss what nesting is and explore some of its advantages. Then, we’ll cover how CSS nesting is used in preprocessors and natively in CSS.

Native CSS nesting browser support

At this time, native CSS nesting has partial support on:

  • Chrome from version 112 through to 119
  • Microsoft Edge from version 112 up to the latest version
  • Safari from version 16.5-17.1
  • Opera from 98 to the latest version

Partial support means the nested selectors must start with a symbol. We’ll explore more on this later on.

Meanwhile, CSS nesting is now fully supported on:

  • Chrome from 120 to the latest version
  • Safari from 17.2
  • Firefox from 117

You can stay up-to-date regarding the browser compatibility of native CSS nesting on CanIUse.

What is CSS nesting?

CSS nesting is the ability to define the style rules of one element inside another. So rather than writing the same selector over and over again to style specific child elements or pseudo-selectors, you can just nest them under a single selector.

Nesting helps you to group related styles and write CSS in a nested hierarchy.

Consider this simple HTML page:

<body> <div class="header"> <h1>Hello!</h1> <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Corrupti nihil, officiis aut vel. <a href="#">This is a Link</a> </p> </div></body>

There’s a header and paragraph inside a div with the class name, .header. There’s also a link inside the p element.

When styling, you may be familiar with writing CSS this way:

.header{ position: absolute; left: 25%; top: 40%; transform: translate(-13%,-42%); color: #22232e;}.header h1{ font-size: 3.5rem;}.header p{ font-size: 2rem; margin: 25px 0px 25px;}.header p a{ color: #00c2cb;}

This is valid, but consider another way of writing these same styles with CSS nesting:

.header { position: absolute; left: 25%; top: 40%; transform: translate(-13%,-42%); color: #22232e; h1 { font-size: 3.5rem; } p { font-size: 2rem; margin: 25px 0px 25px; a { color: #00c2cb; } }}

This syntax should be familiar to developers who use CSS preprocessors like Sass and SCSS. Nesting like this is now possible in native CSS without the help of preprocessors.

If you had another type of selector, like the :hover pseudo-element, here’s how it would be nested:

.header { position: absolute; left: 25%; top: 40%; transform: translate(-13%,-42%); color: #22232e; h1 { font-size: 3.5rem; } p { font-size: 2rem; margin: 25px 0px 25px; a { color: #00c2cb; &:hover { color: #e498db; } } }}

Here’s a CodePen of the HTML page:

See the Pen Page With Sass Nesting by Oscar Jite-Orimiono (@oscar-jite)
on CodePen.

Advantages of CSS nesting

The benefits of CSS nesting include:

  1. Writing more modular and maintainable stylesheets: Rather than having the same selector in multiple places in a stylesheet, you can place all styles related to that selector in one place. This will speed up development and debugging
  2. Nesting media queries: With nesting, there’s no need to have a separate media query rule for a selector. You can have it right where you defined the CSS rules of the selector

Now, let’s explore how we can use CSS nesting in native CSS.

How to nest selectors in CSS

Nesting in native or plain CSS is pretty much the same as we saw when using the preprocessor. However, in some browsers and with certain types of selectors, we must begin with the nesting selector, &.

Here’s a screenshot of the page from the previous example on Opera without the nesting selector:

Native CSS nesting: What you need to know - LogRocket Blog (2)

The style rules of the nested selectors are not applied because this browser doesn’t support nesting without &. So, to give every user the same experience no matter which browser they use, use the ampersand symbol when nesting.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

If we rewrite the CSS code from the previous example with native CSS nesting, we will have the following:

.header { position: absolute; left: 25%; top: 40%; transform: translate(-13%,-42%); color: #22232e; & h1 { font-size: 3.5rem; } & p { font-size: 2rem; margin: 25px 0px 25px; & a { color: #00c2cb; &:hover { color: #3498db; } } }}

As mentioned earlier, the & is needed at the beginning of each selector for the nesting to be valid. We can think of the & as referencing the parent selector. That way, if we invert the above CSS style and replace every & with the parent selector, we will get back the initial CSS structure.

Nesting CSS classes

Let’s take another example. This time, we’ll look at compound selectors like the one below:

h1.header { font-weight: 700}

If we rewrite the code above using native CSS nesting, it looks like this:

h1 { &.header { font-weight: 700 }}

As we can see, replacing the & with the parent selector, h1, gives us back the h1.header. Whenever we add a selector — in this case, a class selector — on the same element, we must ignore the space between & and the selector.

Let’s take another example by rewriting the following style rules using nested classes:

.foo { color: #000000; }.foo .bar { color: #fd2020; }.foo .bar > .baz { color: #ffffff; }

For the first nested class selector, we will have the following:

.foo { color: #000000; & .bar { color: #fd2020; }}

Then, adding the second nested class gives us the below:

.foo { color: #000000; & .bar { color: #fd2020; & > .baz { color: #ffffff; } }}

Another interesting thing about nesting class selectors is that you don’t need & because class names start with the period symbol when you declare them in CSS.

So, if we rewrote the HTML for our page to include classes, like so:

<body> <div class="header"> <h1 class="heading">Hello!</h1> <p class="text">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Corrupti nihil, officiis aut vel. <a href="#" class="link">This is a Link</a> </p> </div></body>

Then we could nest the class selectors without the ampersand:

.header { position: absolute; left: 25%; top: 40%; transform: translate(-13%, -42%); color: #22232e; .heading { font-size: 3.5rem; } .text { font-size: 2rem; margin: 25px 0px 25px; .link { color: #00c2cb; &:hover { color: #3498db; } } }}

The above will work fine on every browser, even without the ampersand. This also applies to other types of selectors: the style rules for the nested selectors are valid as long as they start with the appropriate symbol.

Other selector symbols include # for ids, : and :: for pseudo elements and classes, the * universal selector, sibling selectors + and ~, the > child selector, and finally, the attribute selector [].

Nesting at-rules

The method we explored above also applies to nesting conditional rules such as media queries and feature queries (@supports). Take a look at the following nested rules:

.header { font-size: 40px @media (max-width: 760px ) { & { font-size: 24px; } }}

We can see that replacing the & with the parent selector, .header, gives us the following equivalent:

.header { font-size: 40px;}@media (max-width: 760px) { .header { font-size: 24px; }}

You can also have more than one nested conditional rule:

.header { font-size: 40px @media (orientation: landscape) { & { grid-auto-flow: column; } @media (max-width: 760px ) { & { font-size: 24px; } }}

Other at-rules include @scope, @layer, and @container.

Grouping CSS selectors

Imagine we have a group of selectors such as the following:

#header span,#header a,p span,p a { color: #0000ff;}

First, with the CSS is() function, we can make the style rules a bit compact, like so:

:is(#header, p) span,:is(#header, p) a { color: #ff0000;}

Then, to create nested style rules, we must begin each selector with &. In this case, we will have the following:

#header, p { & span, & a { color: #ff0000; }}

It doesn’t get simpler than this.

CSS nesting and specificity

In CSS, specificity describes a set of rules that determine which styles are applied to an element. If two or more selectors apply to the same element, the one with the highest specificity is applied. CSS cascades, so under normal circ*mstances, the last selector declared in the stylesheet should have more priority.

How is this relevant to CSS nesting? Consider this example of some simple page content with headers:

<body> <section id="header"> <div> <h1 class="heading"> This is a h1 </h1> </div> </section> <section class="text"> <div> <h2>This is a h2</h2> </div> </section></body>

Now, say you want to style the headers using nesting:

#header, .text{ background: #22323e; & h1, & h2 { color: white; }}

Here’s what you should see:

See the Pen Native CSS Nesting And Specificity Demo by Oscar Jite-Orimiono (@oscar-jite)
on CodePen.

Both headings have the color: white style applied. However, note that the h1 has a class name of heading. Imagine that later on in your stylesheet, you decide to use this class name like so:

#header, .text{ background: #22323e; & h1, & h2 { color: white; }}.heading{ color: red;}

Despite this addition to your stylesheet, nothing will happen — both headings will remain white. This is because the equivalent of the nested rules is the following:

:is(#header, .text) h1,h2{ color: white;}

The :is selector takes the specificity of the most specific element, which in this case is the ID selector #header. Its style rules will have higher priority, making them difficult to overwrite later on.

Understanding this behavior is important, as it prevents the need for overly complex selectors or the use of !important to overwrite styles.

Guidelines to follow when using nesting in CSS

So far, we’ve explored in detail how to achieve nesting with a preprocessor as well as native CSS nesting. Now, let’s consider some general rules to follow when nesting style rules.

Avoid over-nesting

Since nesting makes it easy to nest styles, you may be tempted to over-nest selectors. Consider the example below:

main { & section { background-color: red; & ul { background-color: green; & .list { font-size: 16px; & .link { color: pink; & :hover { color: blue } } } } }}

This is equivalent to the following code:

main section { background-color: red;}main section ul { background-color: green;}main section ul .list{ font-size: 16px;}main section ul .list .link{ color: pink;}main section ul .list .link:hover{ color: blue;}

The last selector has six levels of nesting. This can lead to a lot of specificity issues if you ever want to override the styles.

A common rule of thumb is to keep nesting only three levels deep. You can use Stylelint to keep your nesting in check. Use classes with descriptive names as much as possible.

Styles after nested selectors are ignored

When nesting styles in CSS, any styles you define after nested selectors will be ignored. This is good to keep in mind, as it emphasizes the importance of organizing your rules carefully. Consider this example:

main { & section { background-color: red; } color: green;}

The color: green will be ignored since it is written after the nested selector. With CSS nesting, any style rules for the parent element should be declared before the nested selectors. This is one difference between native CSS nesting and nesting with preprocessors.

Conclusion

Nesting is an exciting feature in native CSS that helps keep stylesheets modular and more maintainable. With nesting, all styles related to a selector, children/parent selector, and even media queries can be nested in the same place.

In this article, we explored how nesting works in CSS and why it’s important in modern frontend development, exploring examples with a preprocessor and with native CSS nesting. We also discussed why you should avoid over-nesting and keep specificity in mind while using nesting.

For further study on CSS nesting, you can refer to the W3C draft for the CSS nesting module.

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — start monitoring for free.

Native CSS nesting: What you need to know - LogRocket Blog (2024)

References

Top Articles
Latest Posts
Article information

Author: Carmelo Roob

Last Updated:

Views: 5774

Rating: 4.4 / 5 (65 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Carmelo Roob

Birthday: 1995-01-09

Address: Apt. 915 481 Sipes Cliff, New Gonzalobury, CO 80176

Phone: +6773780339780

Job: Sales Executive

Hobby: Gaming, Jogging, Rugby, Video gaming, Handball, Ice skating, Web surfing

Introduction: My name is Carmelo Roob, I am a modern, handsome, delightful, comfortable, attractive, vast, good person who loves writing and wants to share my knowledge and understanding with you.