CSS Nesting Module

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-nesting/
Latest published version:
https://www.w3.org/TR/css-nesting-1/
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
Tab Atkins-Bittner (Google)
Adam Argyle (Google)
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This module introduces the ability to nest one style rule inside another, with the selector of the child rule relative to the selector of the parent rule. This increases the modularity and maintainability of CSS stylesheets.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-nesting” in the title, like this: “[css-nesting] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list www-style@w3.org.

This document is governed by the 03 November 2023 W3C Process Document.

1. Introduction

This section is not normative.

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.

1.1. Module Interactions

This module introduces new parser rules that extend the [CSS21] parser model. It introduces selectors that extend the [SELECTORS-4] module. It extends and modifies some IDL and algorithms defined in the [CSSOM-1] module.

1.2. Values

This specification does not define any new properties or values.

2. Explainer

This section is non-normative.

Imagine you have some CSS that you’d like to write in a more compact way.

.foo {
  color: green;
}
.foo .bar {
  font-size: 1.4rem;
}

With Nesting, you can write such code as:

.foo {
  color: green;
  .bar {
    font-size: 1.4rem;
  }
}

If you’ve been nesting styles in Sass or other CSS preprocessors, you will find this very familiar.

You can nest any rules inside of a parent style rule:

main {
  div { ... }
  .bar { ... }
  #baz { ...}
  :has(p) { ... }
  ::backdrop { ... }
  [lang|="zh"] { ... }
  * { ... }
}

By default, the child rule’s selector is assumed to connect to the parent rule by a descendant combinator, but you can start the nested selector with any combinator to change that:

main {
  + article { ... }
  > p { ... }
  ~ main { ... }
}

The new & selector lets you refer to the elements matched by the parent selector explictly, so the previous examples could have been written as:

main {
  & + article { ... }
  & > p { ... }
  & ~ main { ... }
}

But you can place the & in other locations within the nested selector, to indicate other types of relationships between the parent and child rule. For example, this CSS:

ul {
  padding-left: 1em;
}
.component ul {
  padding-left: 0;
}

Can be rewritten using Nesting as:

ul {
  padding-left: 1em;
  .component & {
    padding-left: 0;
  }
}

Again, the & gives you a way to say “this is where I want the nested selector to go”.

It’s also handy when you don’t want a space between your selectors. For example:

a {
  color: blue;
  &:hover {
    color: lightblue;
  }
}

Such code yields the same result as a:hover {. Without the &, you’d get a :hover {—​notice the space between a and :hover—​which would fail to style your hover link.

You can nest more than one layer deep—​nesting CSS inside already-nested CSS—​in as many levels as you desire. You can mix Nesting with Container Queries, Supports Queries, Media Queries, and/or Cascade Layers however you want. (Nearly) anything can go inside of anything.

3. Nesting Style Rules

Style rules can be nested inside of other styles rules. These nested style rules act exactly like ordinary style rules—​associating properties with elements via selectors—​but they "inherit" their parent rule’s selector context, allowing them to further build on the parent’s selector without having to repeat it, possibly multiple times.

A nested style rule is exactly like a normal style rule, except that it can use can use relative selectors, which are implicitly relative to the elements matched by the parent rule.

That is, a nested style rule like:
.foo {
  color: red;

  a {
    color: blue;
  }
}

is valid, and equivalent to:

.foo {
  color: red;
}
.foo a {
  color: blue;
}

The nested rule can also use the nesting selector to directly refer to the parent rule’s matched elements, or use relative selector syntax to specify relationships other than "descendant".

.foo {
  color: red;

  &:hover {
    color: blue;
  }
}

/* equivalent to: */

.foo { color: red; }
.foo:hover { color: blue; }
.foo {
  color: red;

  + .bar {
    color: blue;
  }
}

/* equivalent to: */

.foo { color: red; }
.foo + .bar { color: blue; }

3.1. Syntax

The contents of style rules now accepts nested style rules and at-rules, in addition to the existing declarations.

Nested style rules differ from non-nested rules in the following ways:

The precise details of how nested style rules are parsed are defined in [CSS-SYNTAX-3].

An invalid nested style rule is ignored, along with its contents, but does not invalidate its parent rule.

Nested rules with relative selectors include the specificity of their implied nesting selector. For example, .foo { > .bar {...}} and .foo { & > .bar {...}} have the same specificity for their inner rule.

Some CSS-generating tools that preprocess nesting will concatenate selectors as strings, allowing authors to build up a single simple selector across nesting levels. This is sometimes used with hierarchical name patterns like BEM to reduce repetition across a file, when the selectors themselves have significant repetition internally.

For example, if one component uses the class .foo, and a nested component uses .fooBar, you could write this in Sass as:

.foo {
  color: blue;
  &Bar { color: red; }
}
/* In Sass, this is equivalent to
  .foo { color: blue; }
  .fooBar { color: red; }
*/

This is not allowed in CSS, as nesting is not a syntax transformation, but rather matches on the actual elements the parent selector matches.

It is also true that the selector &Bar is invalid in CSS in the first place, as the Bar part is a type selector, which must come first in the compound selector. (That is, it must be written as Bar&.) So, luckily, there is no overlap between CSS Nesting and the preprocessor syntax.

A selector is said to contain the nesting selector if, when it was parsed as any type of selector, a <delim-token> with the value "&" (U+0026 AMPERSAND) was encountered.

Note: This is phrased in this explicit manner so as to catch cases like :is(:unknown(&), .bar), where an unknown selector (which, being unknown, we have no way of knowing whether the argument is meant to be parsed as a selector or not) is the only part of the selector that contains an &. As that might be a perfectly valid selector that’s only supported by newer browsers, and we don’t want parsing to be dependent on unrelated versioning issues, we treat it as still containing the nesting selector.

If a <forgiving-selector-list> has an item that contains the nesting selector but is invalid, that item is preserved exactly as-is rather than being discarded. (This does not change the matching behavior of the selector—​an invalid selector still fails to match anything—​just the serialization of the selector.)

The preceding paragraph needs to move to Selectors when we move & itself to Selectors; I’m monkey-patching for convenience here.

3.2. Examples

/* & can be used on its own */
.foo {
  color: blue;
  & > .bar { color: red; }
  > .baz { color: green; }
}
/* equivalent to
  .foo { color: blue; }
  .foo > .bar { color: red; }
  .foo > .baz { color: green; }
*/


/* or in a compound selector,
   refining the parent’s selector */
.foo {
  color: blue;
  &.bar { color: red; }
}
/* equivalent to
  .foo { color: blue; }
  .foo.bar { color: red; }
*/

/* multiple selectors in the list are all
   relative to the parent */
.foo, .bar {
  color: blue;
  + .baz, &.qux { color: red; }
}
/* equivalent to
  .foo, .bar { color: blue; }
  :is(.foo, .bar) + .baz,
  :is(.foo, .bar).qux { color: red; }
*/

/* & can be used multiple times in a single selector */
.foo {
  color: blue;
  & .bar & .baz & .qux { color: red; }
}
/* equivalent to
  .foo { color: blue; }
  .foo .bar .foo .baz .foo .qux { color: red; }
*/

/* & doesn’t have to be at the beginning of the selector */

.foo {
  color: red;
  .parent & {
    color: blue;
  }
}
/* equivalent to
  .foo { color: red; }
  .parent .foo { color: blue; }
*/

.foo {
  color: red;
  :not(&) {
    color: blue;
  }
}
/* equivalent to
  .foo { color: red; }
  :not(.foo) { color: blue; }
*/

/* But if you use a relative selector,
  an initial & is implied automatically */

.foo {
  color: red;
  + .bar + & { color: blue; }
}

/* equivalent to
  .foo { color: red; }
  .foo + .bar + .foo { color: blue; }
*/

/* Somewhat silly, but & can be used all on its own, as well. */
.foo {
  color: blue;
  & { padding: 2ch; }
}
/* equivalent to
  .foo { color: blue; }
  .foo { padding: 2ch; }

  // or

  .foo {
    color: blue;
    padding: 2ch;
  }
*/

/* Again, silly, but can even be doubled up. */
.foo {
  color: blue;
  && { padding: 2ch; }
}
/* equivalent to
  .foo { color: blue; }
  .foo.foo { padding: 2ch; }
*/

/* The parent selector can be arbitrarily complicated */
.error, #404 {
  &:hover > .baz { color: red; }
}
/* equivalent to
  :is(.error, #404):hover > .baz { color: red; }
*/

.ancestor .el {
  .other-ancestor & { color: red; }
}
/* equivalent to
  .other-ancestor :is(.ancestor .el) { color: red; }

/* As can the nested selector */
.foo {
  & :is(.bar, &.baz) { color: red; }
}
/* equivalent to
  .foo :is(.bar, .foo.baz) { color: red; }
*/

/* Multiple levels of nesting "stack up" the selectors */
figure {
  margin: 0;

  > figcaption {
    background: hsl(0 0% 0% / 50%);

    > p {
      font-size: .9rem;
    }
  }
}
/* equivalent to
  figure { margin: 0; }
  figure > figcaption { background: hsl(0 0% 0% / 50%); }
  figure > figcaption > p { font-size: .9rem; }
*/

/* Example usage with Cascade Layers */
@layer base {
  html {
    block-size: 100%;

    body {
      min-block-size: 100%;
    }
  }
}
/* equivalent to
  @layer base {
    html { block-size: 100%; }
    html body { min-block-size: 100%; }
  }
*/

/* Example nesting Cascade Layers */
@layer base {
  html {
    block-size: 100%;

    @layer support {
      body {
        min-block-size: 100%;
      }
    }
  }
}
/* equivalent to
  @layer base {
    html { block-size: 100%; }
  }
  @layer base.support {
    html body { min-block-size: 100%; }
  }
*/

/* Example usage with Scoping */
@scope (.card) to (> header) {
  :scope {
    inline-size: 40ch;
    aspect-ratio: 3/4;

    > header {
      border-block-end: 1px solid white;
    }
  }
}
/* equivalent to
  @scope (.card) to (> header) {
    :scope { inline-size: 40ch; aspect-ratio: 3/4; }
    :scope > header { border-block-end: 1px solid white; }
  }
*/

/* Example nesting Scoping */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

  @scope (&) to (> header > *) {
    :scope > header {
      border-block-end: 1px solid white;
    }
  }
}

/* equivalent to
  .card { inline-size: 40ch; aspect-ratio: 3/4; }
  @scope (.card) to (> header > *) {
    :scope > header { border-block-end: 1px solid white; }
  }
*/

3.3. Nesting Other At-Rules

In addition to nested style rules, this specification allows nested group rules inside of style rules: any at-rule whose body contains style rules can be nested inside of a style rule as well.

When nested in this way, the contents of a nested group rule's block are parsed as <block-contents> rather than <rule-list>:

Specifically, these rules are capable of being nested group rules:

The meanings and behavior of such nested group rules is otherwise unchanged, unless otherwise specified.

For example, the following conditional nestings are valid:
/* Properties can be directly used */
.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;
  }
}
/* equivalent to
  .foo {
    display: grid;
				
    @media (orientation: landscape) {
      & {
        grid-auto-flow: column;
      }
    }
  }
*/

/* finally equivalent to
  .foo { display: grid; }

  @media (orientation: landscape) {
    .foo {
      grid-auto-flow: column;
    }
  }
*/

/* Conditionals can be further nested */
.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;

    @media (min-width > 1024px) {
      max-inline-size: 1024px;
    }
  }
}

/* equivalent to
  .foo { display: grid; }

  @media (orientation: landscape) {
    .foo {
      grid-auto-flow: column;
    }
  }

  @media (orientation: landscape) and (min-width > 1024px) {
    .foo {
      max-inline-size: 1024px;
    }
  }
*/

/* Example nesting Cascade Layers */
html {
  @layer base {
    block-size: 100%;

    @layer support {
      & body {
        min-block-size: 100%;
      }
    }
  }
}
/* equivalent to
  @layer base {
    html { block-size: 100%; }
  }
  @layer base.support {
    html body { min-block-size: 100%; }
  }
*/

/* Example nesting Scoping */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

  @scope (&) {
    :scope {
      border: 1px solid white;
    }
  }
}

/* equivalent to
  .card { inline-size: 40ch; aspect-ratio: 3/4; }
  @scope (.card) {
    :scope { border-block-end: 1px solid white; }
  }
*/

All directly-nested properties are treated as if they were collected together, in order, and nested in a nested style rule with the selector &, and placed before all other child rules. This includes in the OM. (That is, the cssRules attribute actually starts with this nested style rule, containing all the directly-nested properties.)

For example, the earlier example:

.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;
  }
}
/* equivalent to
  .foo {
    display: grid;

    @media (orientation: landscape) {
      & {
        grid-auto-flow: column;
      }
    }
  }
*/

is in fact exactly equivalent, producing the exact same CSSOM structure. The CSSMediaRule object will have a single CSSStyleRule object in its .childRules attribute, containing the grid-auto-flow property.

Note: This does mean that the serialization of such rules will differ from how they were originally written, with no directly-nested properties in the serialization.

3.3.1. Nested @scope Rules

When the @scope rule is a nested group rule, an & in the <scope-start> selector refers to the elements matched by the nearest ancestor style rule.

For the purposes of the style rules in its body and its own <scope-end> selector, the @scope rule is treated as an ancestor style rule, matching the elements matched by its <scope-start> selector.

That is, the following code:
.parent {
  color: blue;

  @scope (& > .scope) to (& .limit) {
    & .content {
      color: red;
    }
  }
}

is equivalent to:

.parent { color: blue; }
@scope (.parent > .scope) to (.parent > .scope .limit) {
  .parent > .scope .content {
    color: red;
  }
}

3.4. Mixing Nesting Rules and Declarations

When a style rule contains both declarations and nested style rules or nested group rules, all three can be arbitrarily mixed. However, the relative order of declarations vs other rules is not preserved in any way.

For example, in the following code:
article {
  color: green;
  & { color: blue; }
  color: red;
}

/* equivalent to */
article {
  color: green;
  color: red;
  & { color: blue; }
}

For the purpose of determining the Order Of Appearance, nested style rules and nested group rules are considered to come after their parent rule.

For example:
article {
  color: blue;
  & { color: red; }
}

Both declarations have the same specificity (0,0,1), but the nested rule is considered to come after its parent rule, so the color: red declarations wins the cascade.

On the other hand, in this example:

article {
  color: blue;
  :where(&) { color: red; }
}

The :where() pseudoclass reduces the specificity of the nesting selector to 0, so the color: red declaration now has a specificity of (0,0,0), and loses to the color: blue declaration before "Order Of Appearance" comes into consideration.

Note: This behavior both matches many existing preprocessor-based nesting behaviors, and ensures that a rule can be faithfully expressed in the CSSOM without requiring drastic changes to the existing CSSStyleRule object.

Note: While one can freely intermix declarations and nested rules, it’s harder to read and somewhat confusing to do so, since all the properties act as if they came before all the rules. For readability’s sake, it’s recommended that authors put all their properties first in a style rule, before any nested rules. (This also happens to act slightly better in older user agents; due to specifics of how parsing and error-recovery work, properties appearing after nested rules can get skipped.)

Note: Like with other types of rules, the serialization of style rules in the presence of nesting can vary from how they were originally written. Notably, all directly-nested properties will be serialized before any nested rules, which is another reason to write properties before rules.

4. Nesting Selector: the & selector

When using a nested style rule, one must be able to refer to the elements matched by the parent rule; that is, after all, the entire point of nesting. To accomplish that, this specification defines a new selector, the nesting selector, written as & (U+0026 AMPERSAND).

When used in the selector of a nested style rule, the nesting selector represents the elements matched by the parent rule. When used in any other context, it represents the same elements as :scope in that context (unless otherwise defined).

The nesting selector can be desugared by replacing it with the parent style rule’s selector, wrapped in an :is() selector. For example,
a, b {
  & c { color: blue; }
}

is equivalent to

:is(a, b) c { color: blue; }

The nesting selector cannot represent pseudo-elements (identical to the behavior of the :is() pseudo-class).

For example, in the following style rule:
.foo, .foo::before, .foo::after {
  color: red;

  &:hover { color: blue; }
}

the & only represents the elements matched by .foo; in other words, it’s equivalent to:

.foo, .foo::before, .foo::after {
  color: red;
}
.foo:hover {
  color: blue;
}

We’d like to relax this restriction, but need to do so simultaneously for both :is() and &, since they’re intentionally built on the same underlying mechanisms. (Issue 7433)

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

For example, given the following style rules:
#a, b {
  & c { color: blue; }
}
.foo c { color: red; }

Then in a DOM structure like

<b class=foo>
  <c>Blue text</c>
</b>

The text will be blue, rather than red. The specificity of the & is the larger of the specificities of #a ([1,0,0]) and b ([0,0,1]), so it’s [1,0,0], and the entire & c selector thus has specificity [1,0,1], which is larger than the specificity of .foo c ([0,1,1]).

Notably, this is different than the result you’d get if the nesting were manually expanded out into non-nested rules, since the color: blue declaration would then be matching due to the b c selector ([0,0,2]) rather than #a c ([1,0,1]).

Why is the specificity different than non-nested rules?

The nesting selector intentionally uses the same specificity rules as the :is() pseudoclass, which just uses the largest specificity among its arguments, rather than tracking which selector actually matched.

This is required for performance reasons; if a selector has multiple possible specificities, depending on how precisely it was matched, it makes selector matching much more complicated and slower.

That skirts the question, tho: why do we define & in terms of :is()? Some non-browser implementations of Nesting-like functionality do not desugar to :is(), largely because they predate the introduction of :is() as well. Instead, they desugar directly; however, this comes with its own significant problems, as some (reasonably common) cases can accidentally produce massive selectors, due to the exponential explosion of possibilities.

.a1, .a2, .a3 {
  .b1, .b3, .b3 {
    .c1, .c2, .c3 {
      ...;
    }
  }
}

/* naively desugars to */
.a1 .b1 .c1,
.a1 .b1 .c2,
.a1 .b1 .c3,
.a1 .b2 .c1,
.a1 .b2 .c2,
.a1 .b2 .c3,
.a1 .b3 .c1,
.a1 .b3 .c2,
.a1 .b3 .c3,
.a2 .b1 .c1,
.a2 .b1 .c2,
.a2 .b1 .c3,
.a2 .b2 .c1,
.a2 .b2 .c2,
.a2 .b2 .c3,
.a2 .b3 .c1,
.a2 .b3 .c2,
.a2 .b3 .c3,
.a3 .b1 .c1,
.a3 .b1 .c2,
.a3 .b1 .c3,
.a3 .b2 .c1,
.a3 .b2 .c2,
.a3 .b2 .c3,
.a3 .b3 .c1,
.a3 .b3 .c2,
.a3 .b3 .c3 {...}

Here, three levels of nesting, each with three selectors in their lists, produced 27 desugared selectors. Adding more selectors to the lists, adding more levels of nesting, or making the nested rules more complex can make a relatively small rule expand into multiple megabytes of selectors (or much, much more!).

Some CSS tools avoid the worst of this by heuristically discarding some variations, so they don’t have to output as much but are still probably correct, but that’s not an option available to UAs.

Desugaring with :is() instead eliminates this problem entirely, at the cost of making specificity slightly less useful, which was judged a reasonable trade-off.

The nesting selector is capable of matching featureless elements, if they were matched by the parent rule.

While the position of a nesting selector in a compound selector does not make a difference in its behavior (that is, &.foo and .foo& match the same elements), the existing rule that a type selector, if present, must be first in the compound selector continues to apply (that is, &div is illegal, and must be written div& instead).

5. CSSOM

Note: [CSSOM-1] now defines that CSSStyleRule can have child rules.

When serializing a relative selector in a nested style rule, the selector must be absolutized, with the implied nesting selector inserted.

When serializing a nested group rule, it must serialize solely with child rules.

For example, the selector > .foo will serialize as & > .foo.

A nested group rule like:

.foo {
  @media (prefers-color-scheme: dark) {
    color: white;
    background: black;
  }
}

will serialize as:

.foo {
  @media (prefers-color-scheme: dark) {
    & {
      color: white;
      background: black;
    }
  }
}

Note: These rules ensure that the rules in question are valid to be moved to other contexts, including non-nested ones. It also ensures that the serialization closely matches the structure of the CSSOM.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">, like this: UAs MUST provide an accessible alternative.

Tests

Tests relating to the content of this specification may be documented in “Tests” blocks like this one. Any such block is non-normative.


Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet.
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.

A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)

An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.

Partial implementations

So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.

Implementations of Unstable and Proprietary Features

To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

Non-experimental implementations

Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.

To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/. Questions should be directed to the public-css-testsuite@w3.org mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS Cascading and Inheritance Level 4. URL: https://drafts.csswg.org/css-cascade-4/
[CSS-CASCADE-6]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 6. URL: https://drafts.csswg.org/css-cascade-6/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. URL: https://drafts.csswg.org/css-color/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://drafts.csswg.org/css-syntax/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. URL: https://drafts.csswg.org/selectors/

Informative References

[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
[CSS-CONDITIONAL-3]
David Baron; Elika Etemad; Chris Lilley. CSS Conditional Rules Module Level 3. URL: https://drafts.csswg.org/css-conditional-3/
[CSS-CONTAIN-3]
Tab Atkins Jr.; Florian Rivoal; Miriam Suzanne. CSS Containment Module Level 3. URL: https://drafts.csswg.org/css-contain-3/
[CSS-GRID-2]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 2. URL: https://drafts.csswg.org/css-grid-2/
[CSS-UI-4]
Florian Rivoal. CSS Basic User Interface Module Level 4. URL: https://drafts.csswg.org/css-ui-4/

Issues Index

The preceding paragraph needs to move to Selectors when we move & itself to Selectors; I’m monkey-patching for convenience here.
We’d like to relax this restriction, but need to do so simultaneously for both :is() and &, since they’re intentionally built on the same underlying mechanisms. (Issue 7433)