DEV Community

Cover image for Getting started with CSS Nesting
Lazar Nikolov for creatures.dev

Posted on • Originally published at creatures.dev

Getting started with CSS Nesting

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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): {
  /* ... */
}
Enter fullscreen mode Exit fullscreen mode

Nesting pseudo-elements

article.card {
  /* ... */

  & footer a {
    /* ... */

    &:hover {
      background: maroon;

      &::after {
        content: '>';
        margin-left: 12px;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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) {
    /* ... */
  }
}
Enter fullscreen mode Exit fullscreen mode

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 */
  }
}
Enter fullscreen mode Exit fullscreen mode

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 */
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

& @ : . > ~ + # [ *
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
pavelee profile image
Paweł Ciosek

Nice! Love that on native css! What’s that mean for preprocessors future like sass or less? 🙈