Skip to content

[css-nesting-1] Should code portability trump coding habits wrt nesting syntax? #8029

@FremyCompany

Description

@FremyCompany

As a follow-up from #7834, the question of the "same-brace-but-inconsitent-prefixing" (Proposal 3) vs "other-brace-but-consistent-prefixing" (Proposal 4) was left open. Proposal 3 is described in the current editor draft. The presentation of Proposal 4 and the related discussion can be found here: #7834 (comment). Proposal 5 can be found here: #7970

The main advantage of Proposal 3...

is that this should be very familiar to anyone who might have used nesting through CSS preprocessors, although the requirement to use & or :is() in some cases is new as preprocessors don't limit themselves to the more efficient types of parsing which browsers rely on for performance reasons.

The main advantage of Proposal 4...

is that it provides full interoperability between contexts: you can take any CSS code you have today, wrap it in a block, add a selector prefix, and now you have "nested" the existing code without having to make any change. Similarly, moving from and to CSS Scoping blocks does not require any change at all. Finally, if some code needs to be un-nested (because it is moved to a Web Components CSS file and no longer requires a prefix), this can be done without any change to the CSS.

The main advantages of Proposal 5...

It provides a top-level construct that contains nothing but nested rule. This avoids the need to mix style rules and properties in a single context. It uses nothing but standard CSS syntax and requires no changes to the OM or parsing rules. It's easier to explain and understand.

Apart from the requirement to put the rules in another brace, the syntax between the three proposals looks remakably simliar, as can be seen in the following examples:

Proposal 3Proposal 4Proposal 5
  • Is familiar to users of CSS preprocessors.
  • Every CSSStyleDeclaration block can now support nested rules
  • Code portability between regular stylesheets rules, nested rules, scoped rules, and web component rules.
  • Does not put major syntax restrictions on the declaration block.
  • Code portability between regular stylesheet rules, nested rules, scoped rules, and web component rules.
  • No new syntax, parsing changes or OM changes.
  • Avoids potential conflicts of future property or selector syntax exensions.
  • Preserves the ability to interleave nested rules without reordering in the OM.
  • Rules are invalid if they start with a type selector, requiring them to be rephrased somehow. (Using :is(div), starting with &, etc.)
  • This prevents us from changing property syntax to start with an ascii glyph in the future.
  • Requires another pair of brackets
  • If you are only nesting rules, you still need an empty declaration block ({}), which looks awkward
  • Nesting is not secured by default for the others CSSStyleDeclaration context (e.g. inline styles)
  • Requires adding an additional block of scope to the parent rule
Spec example 1...
table.colortable {
  & td {
    text-align:center;
    &.c { text-transform:uppercase }
    &:first-child, &:first-child + td { ... }
  }
  & th {
    text-align:center;
    background:black;
    color:white;
  }
}
table.colortable {} {
  td { text-align:center; } {
    &.c { text-transform:uppercase }
    &:first-child, &:first-child + td { ... }
  }
  th {
    text-align:center;
    background:black;
    color:white;
  }
}
@nest table.colortable {
  @nest td {
    { text-align:center; }
    &.c { text-transform:uppercase }
    &:first-child, &:first-child + td { ... }
  }
  th {
    text-align:center;
    background:black;
    color:white;
  }
}
Spec example 2...
.foo {
  color: red;
  .bar {
    color: blue;
  }
}
.foo { 
  color: red; } 
  { .bar {
    color: blue;
  }
}
@nest .foo {
  { color: red; }
  .bar { 
    color: blue; 
  }
}
Spec example 3...
div {
  color: red;

  & input { margin: 1em; }
}
div {
  color: red; } {

  input { margin: 1em; }
}
@nest div {
  { color: red; }

  input { margin: 1em; }
}
Integration with layers...
@layer base {
  html {
    block-size: 100%;

    @layer base.support {
      & body {
        min-block-size: 100%;
      }
    }
  }
}
@layer base {
  html {
    block-size: 100%; } {
    
    @layer base.support {
      body {
        min-block-size: 100%;
      }
    }
  }
}
@layer base {
  @nest html {
    { block-size: 100%; }

    @layer base.support {
      body {
        min-block-size: 100%;
      }
    }
  }
}
Integration with scope...
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

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

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

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

Regarding the question of nested rules in the style attribute, I would rather have rules contained in a <style> element where they can receive the space needed. There is a proposal for this already, for instance:

<article>
    <style>
        :style-root { } {
            h1 { color: purple; }
        }
    </style>
    <h1>Title</h1>
    ....
</article>

Otherwise, we can also put them in another attribute named cssRules="..." which contains the content of the second (rule) block.

Anything I missed in the discussion?

Metadata

Metadata

Labels

Closed as RetractedWhen the person who raised the issue thinks that there's no issue after all.css-nesting-1Current Work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions