惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
D
DataBreaches.Net
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
B
Blog
U
Unit 42
有赞技术团队
有赞技术团队
博客园 - 聂微东
GbyAI
GbyAI
宝玉的分享
宝玉的分享
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MyScale Blog
MyScale Blog
Jina AI
Jina AI
Martin Fowler
Martin Fowler
IT之家
IT之家
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
P
Proofpoint News Feed
A
About on SuperTechFans
I
InfoQ
博客园 - 【当耐特】
C
Check Point Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
Y
Y Combinator Blog
Project Zero
Project Zero
WordPress大学
WordPress大学
小众软件
小众软件
AWS News Blog
AWS News Blog
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
I
Intezer
Engineering at Meta
Engineering at Meta
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes

Ahmad Shadeed

Better fluid sizing with round() Use Cases for Field Sizing The Basics of Anchor Positioning Item Flow CSS Relative Colors Balancing Text In CSS Should masonry be part of CSS grid? CSS display contents CSS Grid Areas CSS Cap Unit An Interactive Guide to CSS Container Queries CSS :has() Interactive Guide CSS Nesting UX in DevTools Future CSS: State Container Queries Rebuilding a comment component with modern CSS Conditional CSS with :has and :nth-last-child CSS Text balancing with text-wrap:balance CSS Masking Do we need CSS flex-wrap detection? My CSS Wishlist Conditional CSS CSS Style Queries Inside the mind of a frontend developer: Article layout Inside the mind of a frontend developer: Hero section CSS container queries are finally here The CSS behind Figma First Look At The CSS object-view-box Property Learn CSS Subgrid CSS :has Parent Selector Aligning Content In Different Wrappers Flexbox Dynamic Line Separator Hello, CSS Cascade Layers Building UI Components With SVG and CSS A Deep CSS Dive Into Radial And Conic Gradients Defensive CSS Building Real-life Components: Facebook Messenger Conditional Border Radius In CSS CSS Container Query Units Less Absolute Positioning With Modern CSS Aligning a Button Label Vertically Comparing Design Mockups To Code Result Using HSL Colors In CSS Custom Scrollbars In CSS Let CSS Container Queries For Designers The State of CSS Cross-Browser Development Overflow Issues In CSS Inspect Element As A Way To Increase Your Curiosity Handling Text Over Images in CSS Digging Into CSS Logical Properties Clipping Scrollable Areas On The inline-start Side Understanding Clip Path in CSS The Art of Building Real-life Components Handling Short And Long Content In CSS CSS Scroll Snap A Deep Dive Into CSS Grid minmax() CSS Variables 101 Finding The Root Cause of a CSS Bug Learn CSS centering How to detect browser support for Flexbox Gap CSS Mistakes While On Autopilot Digging Into the Flex Property Understanding CSS Multiple Backgrounds Aligning Logo Images in CSS Grid for layout, Flexbox for components Colors in CSS Thinking About The In-between Design Cases min(), max(), and clamp() CSS Functions Image Techniques On The Web Everything About Auto in CSS Learn Box Alignment Let Learn CSS Positioning Intrinsic Sizing In CSS CSS Grid Template Areas In Action Hiding Elements On The Web Creating a Variable Color Font From Scratch Building a Football Ticket With CSS and SVG Blending Modes in CSS CSS Variables With Inline Styles Implementing Dark Mode For My Website Rebuilding Apple Music Header in HTML & CSS Accessible Checkbox Layout Flickering On Browser Resize Enhancing The Clickable Area Size Custom Underlines with SVG Part 3: The Process of Implementing A UI Design From Scratch Part 2: The Process of Implementing A UI Design From Scratch Building An Old Nav Design CSS Flexbox: 5 Real World Use Cases I Used CSS Inline Flex For The First Time The Process of Implementing A UI Design From Scratch Common CSS Issues For Front-End Projects Handling Long and Unexpected Content in CSS How to Build Web Form Layouts With CSS Grid Grid Layout Ah-ha Moment Enhancing Our Components with CSS :empty Building Resizeable Components with Relative CSS Units CSS Writing Mode The Journey of Learning Front End Web Development on a Daily Basis
CSS Nesting
Ahmad Shadeed · 2023-11-19 · via Ahmad Shadeed

The Layout Maestro

I spent years teaching CSS layout on this blog. I put everything I know into The Layout Maestro course: 70+ lessons and 150+ interactive examples that teach you how to think CSS layouts, not just memorize syntax.

Get the course

If you are a front-end developer who worked with a CSS pre-pre-processor, then you’ve probably come across or used the nesting feature. It has been a popular feature and for me, it’s been one of the features that kept me using a CSS preprocessor..

This year, native CSS nesting got supported in all major browsers: Chrome, Firefox, and Safari. This is a major CSS feature that will make it easier to write CSS. In this article, I will document what I learned so far about CSS nesting and share my findings with you, along with use cases and examples.

There is no prerequisite to follow along except for your excitement and focus.

Introduction

Nesting in CSS has been one of the long-awaited features for many developers. We used to rely on CSS pre-processors like Sass or Less. Let me give you a quick review:

Consider the following example. We have an icon that is nested within the selector .nav__item.

.nav__item {
  .icon {
    display: flex;
    padding: 1rem;
  }
}

The above is a valid Sass code. When compiled, it will look like the following to the browser:

.nav__item .icon {
  display: flex;
  padding: 1rem;
}

With native CSS nesting, the same CSS will work as is. Here is a figure that shows a comparison between native CSS nesting and the browser DevTools.

Notice how the browser is showing the CSS similar (almost) to how it’s displayed in the CSS.

If this CSS was compiled in Sass, the browser will show it like this:

Benefits of CSS nesting

There are some valid reasons in my opinion which makes nesting CSS useful:

  • Easier to read CSS.
  • Grouping style together.
  • Scoping specific styles.
  • Styling HTML elements that don’t have a class or ID.

CSS nesting rules

To educate you about CSS nesting, I will try to provide visual examples of different CSS problems and how nesting can help solve them.

First of all, you need to learn about the ampersand symbol &. There are multiple conditions where this symbol is necessary.

Nesting an element without a class or ID

In this example, the <a> element is styled via the .nav__item. Using the ampersand symbol is optional for the CSS to be valid.

.nav__item {
  & a {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* Same as: */
.nav__item a {
}

You can also opt out of the ampersand:

.nav__item {
  a {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* Same as: */
.nav__item a {
}

Note that this is a recent update and is called Relaxed CSS nesting. It works in the latest Chrome Canary and Safari Technology Preview. Check out this post on relaxed nesting by Adam Argyle.

Nesting an element with a class

Take the same previous example, but assume that the <a> element has an HTML class.

.nav__item {
  .link {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* Same as: */
.nav__item .link {
}

No need to use the ampersand here. The class name will work fine.

Nesting CSS combinators

One of the benefits of CSS native nesting is using combinators. Let’s take a few examples.

In the following example, I want to select every element with the class .nav__item that is preceded by another element with the same class. For that purpose, I used the adjacent sibling selector.

In native CSS nesting, we can use the ampersand symbol to mimic that. Notice that I repeated it twice.

.nav__item {
  & + & {
    border-left: 2px solid;
  }
}

The magic happens in the 2nd repetition of the ampersand. Here, the browser will understand that I want to use the adjacent sibling selector. Let me show you a figure that illustrates this:

Another example is nesting the child combinator. It can select the direct child of an element.

.nav__item {
  > a {
    padding: 1rem;
  }
}

The ampersand symbol

.nav__item {
  & a {
    color: blue;
  }
}

This was required in the initial CSS nesting spec. In Safari TP 179+ and Chrome Canary 120, the ampersand symbol is no longer required for nesting elements.

As a result, the following works:

.nav__item {
  a {
    color: blue;
  }
}

The only problem is that you have to fallback on the previous version of the spec, which must include the ampersand & symbol.

Nesting by example: active, focus, hover

The :active, :focus, and :hover is CSS pseudo-classed that are activated via user action.

With CSS nesting, it’s possible to nest them all at once to avoid code duplication. Let’s take :hover as an example:

button {
  &:hover {
    background-color: var(--bg-color);
  }

  &:focus {
    outline: solid 2px;
  }
}

The difference when using a pre-processer for nesting is that the browser will render it like this:

button:hover {
  background-color: var(--bg-color);
}

button:focus {
  outline: solid 2px;
}

Let’s take a look at how the native CSS nesting is rendered in Chrome, Safari, and Firefox.

I have a few thoughts about the DevTools UX for CSS nesting and will discuss them later in the article.

Nesting by example: post content

One of the first examples to experiment with CSS nesting is styling a post’s body content. Imagine an article with body headlines, text, images, quotes, and more.

The headings

We tend to style headings like the following:

.post-content h1,
.post-content h2,
.post-content h3,
.post-content h4 {
  /* styles here */
}

With CSS nesting, it’s more straightforward:

.post-content {
  h1,
  h2,
  h3,
  h4 {
    color: var(--heading-color);
    font-weight: var(--heading-font-bold);
    margin-bottom: var(--size-2);
  }
}

We can also do the same by using the :is() selector.

.post-content {
  :is(h1, h2, h3, h4) {
    color: var(--heading-color);
    font-weight: var(--heading-font-bold);
    margin-bottom: var(--size-2);
  }
}

The paragraph element

A common case is to style a link that is inside a paragraph. In such a case, CSS nesting works great.

.post-content {
  & p {
    color: var(--color-black);

    & a {
      font-weight: bold;
      text-decoration: underline;
    }
  }
}

The link might need a hover or a focus effect, too.

.post-content {
  & p {
    color: var(--color-black);

    & a {
      font-weight: bold;
      text-decoration: underline;

      &:hover {
        /* hover styles */
      }
    }
  }
}

We can also nest media queries.

.post-content {
  & p {
    /* base styles */

    @media (min-width: 400px) {
      /* do something */
    }
  }
}

In some cases, a CMS might wrap a <p> element within another element, and for styling purposes, you need to only style the direct <p> elements.

.post-content {
  /* Select the direct <p> elements */
  > p {
    /* base styles */
  }
}

The block quote

In this example, the quote gets its custom styling, and the <p> element within the quote is selected to reset the bottom margin to zero.

.post-content {
  & blockquote {
    /* custom quote styling */

    & p {
      margin-bottom: 0;
    }
  }
}

The post figure

The post figure contains an image and an optional <figcaption> to show a description of the image.

In my example, I need to style the <figure> differently if it has a caption. By nesting CSS :has(), this is possible.

.post-content {
  & figure {
    & img {
      /* the figure's image styles */
    }

    /* changes to the <figure> container, if it has a figcaption element */
    &:has(figcaption) {
      display: flex;
      align-items: start;
    }

    & figcaption {
      /* caption styling */
    }
  }
}

The post list

I need to add a border to all list items except the last one. For that purpose, I used the :not() selector.

.post-content {
  li {
    &:not(:last-child) {
      border-bottom: 1px solid;
    }
  }
}

To use :not(), we need to append an ampersand before it.

Custom spacing for headings

I need to reduce the spacing below <h3> and <h4> if one of them is followed by a code snippet.

.post-content {
  & h3 + [class*="language-"],
  & h4 + [class*="language-"] {
    margin-top: 0.5rem;
  }
}

As you’ve seen in this practical example, the use of CSS nesting is straightforward, especially if you’re coming from a CSS preprocessor experience.

Nesting by example: card component

I will demonstrate a simple card component that uses CSS nesting to achieve the desired styles.

Assuming that there is a .card element with default or basic styles, I will move on to showcase the use of CSS nesting.

.card {
  /* default card styles */
}

Nesting container queries

If the container width is bigger than 400px, I want the card to become a flex container.

.card {
  /* default card styles */

  /* if the container width is 400px or bigger */
  @container card (min-width: 400px) {
    display: flex;
  }
}

Styling the paragraph element

I want to style the paragraph element via the <h3>. That way, I can add margins and paddings to the <p> element. If it’s not there, the UI won’t have extra spacing.

.card__content {
  & h3 + p {
    border-top: 1px solid #000;
    padding-top: 0.5rem;
    margin-top: 0.5rem;
  }
}

When the container width is 400px or larger, the .card__content element should become a flex container, too.

.card__content {
  & h3 + p {
    border-top: 1px solid #000;
    padding-top: 0.5rem;
    margin-top: 0.5rem;
  }

  @container card (min-width: 400px) {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

Nesting by example: form input

A common case is to style the placeholder of an input. The problem is that each browser vendor has its prefix (oh, it’s 2023).

Since the prefix styles need a double colon, we need to use the ampersand &, otherwise the styles will break.

input {
  --placeholder-color: #969696;
  /* other styles */

  &::-webkit-input-placeholder {
    color: var(--placeholder-color);
  }

  &::-moz-placeholder {
    color: var(--placeholder-color);
    opacity: 1;
  }

  &:-moz-placeholder {
    color: var(--placeholder-color);
  }
}

You might be wondering, what’s the difference between using CSS nesting, or directly writing the prefix style without it.

/********** Option 1: native nesting **********/
input {
  &::-webkit-input-placeholder {
    color: var(--placeholder-color);
  }
}

/********** Option 2: without nesting **********/
input::-webkit-input-placeholder {
  color: var(--placeholder-color);
}

There is no difference between the two. Both have the same specificity (0, 1, 1).

Nesting by example: style an element via its parent

We can use native CSS nesting to change a child’s styles based on where it lives. For example, if the .button element lives within a .box parent, it should take the full width.

<div class="box">
    <h2>Get access to all features</h2>
    <p>Create an account now and get access to all exclusive features.</p>
    <a href="/offer" class="button">Create an account</a>
</div>
.button {
  .box & {
    width: 100%;
  }
}

/* equivalent to */
.box .button {
}

Bugs I spotted while exploring CSS nesting

Using the universal selector without the ampersand

Say that we have a card and we want to select all the elements within it. With CSS native nesting, this should work:

.card {
  * {
    /* styles here */
  }
}

I found out that this doesn’t work in Chrome stable, but works fine in Chrome Canary 121, Safari 17.1, and Firefox 119.

The fix is to append an ampersand.

.card {
  & * {
    /* styles here */
  }
}

Using data attributes without the ampersand

In this issue, selecting a data attribute without the ampersand won’t render the expected result.

.card {
  [data-type="featured"] {
    /* styles here */
  }
}

I found out that this doesn’t work in Chrome stable, but works fine in Chrome Canary 121, Safari 17.1, and Firefox 119.

To fix that, we need to append an ampersand:

.card {
  &[data-type="featured"] {
    /* styles here */
  }
}

Both of those bugs were fixed in the release for the relaxed CSS nesting in Chrome Canary.

Detecting support for CSS nesting

It’s possible to use @supports to check for CSS nesting support. In our case, we want to check if the browser identifies the ampersand or not.

@supports selector(&) {
  .post-content {
    & h2 {
      /* styles here. */
    }
  }
}

You can explore other options to detect support in this Codepen by Bramus.

For me, I will use the PostCSS nesting plugin today, which compiles native CSS nesting to normal CSS. Once it’s safe to use, I will drop off the plugin.

I’m not a big fan of the current UX for CSS nesting in the browser DevTools.

I worked on a separate article where I explored a few proposals for how I would like CSS nesting to be in the DevTools.

Check out the article

Conclusion

CSS nesting is a major feature that will enhance how we write CSS. For the time being, using nesting is possible but you need to be careful about the audience, as the support is still new.

I hope that you’ve learned something useful. Thank you for reading!

Further resources