The future of CSS: Nesting Selectors

Early March the first Editor’s Draft for the CSS Nesting Module was published. The draft outlines a future mechanism by which we’ll be able to nest CSS selectors natively (e.g. in pure CSS, without the use of any preprocessors)

This module describes support for nesting a style rule within another style rule, allowing the inner rule’s selector to reference the elements matched by the outer rule. This feature allows related styles to be aggregated into a single structure within the CSS document, improving readability and maintainability.

🤔 Editor's Draft?

A Editor's Draft (ED) is an unofficial version of a specification. They have no official standing, and do not necessarily imply consensus of the CSS Working Group. These Editor's Drafts can describe modules that are not part of the Recommendation Track yet, or can contain in-progress versions of Modules that already are part of the Rec Track.

If an Editor's Draft gets picked up by the CSSWG, a “First Public Working Draft” gets published. This makes the Module officially part of the Rec Track and marks the start of the Working Draft (WD) phase. From there on the proposal can advance further down the Recommendation Track.

In visual form, the Recommendation Track looks something like this:

See An Inside View of the CSS Working Group at W3C for more details on all phases.

UPDATE 2021.07.07: 🥳 The CSSWG has agreed to publish the First Public Working Draft of this module, making it part of the Recommendation Track! Things are getting official, folks!

UPDATE 2021.08.31: The First Public Working Draft got published.

UPDATE 2022.07.26: There is a poll about the syntax running over at developer.chrome.com. Please vote if you want to be heard on this.

~

Table of Contents

~

# Direct nesting with the & selector

Using the & selector (read this as “the nesting selector”), you can refer to the selector from the parent rule.

Instead of writing this …

table.colortable td {
  text-align:center;
}
table.colortable td.c {
  text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
  border:1px solid black;
}
table.colortable th {
  text-align:center;
  background:black;
  color:white;
}

… you can write this thanks to CSS Nesting:

table.colortable {
  & td {
    text-align: center;
    &.c { text-transform: uppercase }
    &:first-child, &:first-child + td { border: 1px solid black }
  }
  & th {
    text-align: center;
    background: black;
    color: white;
  }
}

Cool! 😎

~

# Caveats

# The & selector is mandatory when nesting

When nesting in CSS, you always need to use the & selector. So you won’t be able to do this:

/* INVALID, missing & */
.foo {
  .bar {
    color: hotpink;
  }
}

The fix would be to write & .bar instead of .bar

# Declarations must become before nesting selectors

If the outer selector also contains some CSS properties, they must go before the nesting selector-part. In the example below the rules after the inner selector will simply be ignored:

.foo {
  background: lime;
  & .bar {
    color: hotpink;
  }
  font-weight: 700; /* INVALID, should go before “& .bar” */
}

The fix would be to place the font-weight: 700; declaration before the & .bar block.

# The & selector must be the first compound selector when direct nesting

The & selector can also only be used when it’s first compound selector (e.g. it appears at the first position) of an inner selector.

/* INVALID, & should come first */
.foo {
  .bar & {
    color: hotpink;
  }
}

The fix would be to use an alternative syntax (see further down)

💡 This requirement is put in place so that the parse can unambiguously determine what we’re talking about. As explained in the spec:

The syntax of a selector overlaps with the syntax of a declaration. For example, if a parser starts by seeing color:hover, it can’t tell whether that’s the color property (being set to an invalid value…) or a selector for a <color> element.

Makes sense.

# Specificity

Note that the nesting selector will take over the specificity from its enclosing selector list.

The specificity of the nesting selector is equal to the largest specificity among the complex selectors in the parent style rule’s selector list.

When you have this piece of code …

#element, div {
  & p { color: blue; }
}
.foo p { color: red; }

… the text of div.foo p will be blue.

That’s because the specificity for & here is (1,0,0) — the highest of #element ((1,0,0)) or div ((0,0,1)). Therefore the specificity for & p is (1,0,1), which wins from the specificity of .foo p ((0,1,1))

This behavior is similar to how the specificity of :is() is calculated.

~

# An alternative syntax: the @nest rule

To cater for that third caveat, you can to use the @nest rule syntax, which is more lax. It still requires a & selector, but the selector does not need to appear first:

/* VALID */
.foo {
  @nest .bar & {
    color: hotpink;
  }
}

Aside from the slight difference in how they’re written, both the @nest rule and “direct nesting” are exactly equivalent in functionality.

~

# Browser Support

Don’t expect this to land in browsers any time soon, as the spec is still in its early stages.

Data on support for the css-nesting feature across the major browsers from caniuse.com

💡 Shown above is a dynamic CanIUse.com image, showing an always up-to-date support table. By the time you are reading this browser support might have become better.

To follow along with browser development, you can follow these Issues:

~

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

2 Comments

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.