Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-syntax][css-nesting] Design of @nest rule #10234

Closed
LeaVerou opened this issue Apr 20, 2024 · 75 comments · Fixed by #10386
Closed

[css-syntax][css-nesting] Design of @nest rule #10234

LeaVerou opened this issue Apr 20, 2024 · 75 comments · Fixed by #10386
Labels
css-nesting-1 Current Work

Comments

@LeaVerou
Copy link
Member

LeaVerou commented Apr 20, 2024

Opening this as requested by @astearns

In #8738 we resolved to stop hoisting interleaved declarations and introduce an @nest rule that means "exactly the same thing as the parent" instead of wrapping in :is(), which is how interleaved declarations will be represented in the CSS OM. Since we were not able to get consensus on the specifics, but we had consensus that any solution along these lines is better than the status quo, we agreed that Tab would spec whatever (commit here), and we'd discuss the details later, since fixing the specifics is more web compatible than changing the current behavior after even longer.

Note

An interleaved declaration is a declaration that comes after one or more nested rules.

The issues around which we could not reach consensus were:

  1. If authors have no reason to write @nest and it’s only introduced to represent interleaved declarations and rules, should they even be able to?
  2. If @nest rules are magically added around interleaved declarations should they also be removed during serialization?
    1. If they are removed during serialization, does this happen always, or only when part of a larger rule (e.g. as part of .cssText)?
  3. Do we even need a new @nest rule? What if we simply use the existing CSSStyleDeclaration object to represent interleaved rules? (proposed by @mdubet)
  4. How does setProperty() work if we go with one of the designs that involve more magic?
  5. What happens when a rule is removed and thus two sets of interleaved declarations become adjacent?
    1. Similar issue not brought up in the call: what about the case when these rules are first? Should they be merged with rule.style?

These are not orthogonal decisions: it seems clear that if @nest serializes to include an actual @nest {} rule, that @nest rule needs to also be valid author code. So essentially there are three possible designs:

  1. Magic-minimizing @nest (proposed by @tabatkins, supported by @emilio @andruud @Loirooriol): The rule is automatically added around interleaved declarations, but there is no more magic besides that.
  2. Author-exposure minimizing @nest (proposed by @LeaVerou, supported by @fantasai @astearns): The rule becomes a CSS OM detail, with no corresponding CSS syntax, and is removed on serialization (regardless of how serialization happens).
  3. No @nest, just CSSStyleDeclaration in the CSSOM (proposed by @mdubet, supported by @LeaVerou @fantasai).
    • Criticism: That means .cssRules will also return non-rules? Would .insertRule() also accept CSSStyleDeclaration?

For 2 and 3, there are also design variations based on the answer to 4 and 5 above.

My position:

  • As a design principle, I don't think CSS should have author-facing syntax that provides no benefit to authors. Either we should come up with actual use cases for @nest or make it an internal detail. That said, there could conceivably be use cases for it. E.g. one of the problems with IACVT is that fallbacks are thrown away by the time the declaration becomes invalid. What if this was a way to preserve fallbacks?
  • Despite proposing 2, I actually now support 3 as I realized this version of @nest is functionally equivalent to CSSStyleDeclaration and we should not be introducing new interfaces with philosophical purity being the only motivation (i.e. "but otherwise .cssRules would be returning a non-rule?!?")
  • Some voices are arguing for less magic as a goal in itself. I don't think avoiding magic at all costs should be a goal; the right magic can make a huge usability difference (at the cost of implementation complexity). It’s the wrong kind of magic that becomes problematic: magic that rarely predicts user intent well, and especially when they cannot opt-out of it. Neither seems to be the case here, at least if we design this well.
  • I think authors should be able to interact with the CSS OM predictably without having to care whether there are interleaved declarations or not. This means that setProperty() on the base rule should just work, without them having to do tree walking to find the last nested rule. Like, setProperty() is an incredibly common operation, and an API where calls to setProperty() have no effect are exactly the kind of dreadful APIs that make utility libraries proliferate.
  • I strongly disagree with Tab that reading and modifying the CSS OM only affects about a hundred developers in the entire world (!). Reading and modifying the CSS OM is at the core of a ton of very widespread libraries (e.g. jQuery, D3, etc). Even if most authors do not use it directly, I assure you almost every developer reads and modifies the CSS OM regularly, it's just done through several layers of abstractions.

So I would propose a design that would minimize author exposure to all of this, and would just try to do what's reasonable when reading and modifying the CSS OM:

  • .cssRules would contain CSSStyleDeclaration objects with interleaved declarations
  • insertRule() would accept CSSStyleDeclaration objects
  • No nonsensical or redundant structures:
    • Adjacent CSSStyleDeclaration objects would be merged.
    • A CSSStyleDeclaration object cannot be at the start of cssRules. Inserting one should simply merge the declarations with rule.style.

Should rule.style be magic?

One thing I'm ambivalent about is whether rule.style should be magic too.
This would mean:

  • rule.style returns the union of all interleaved declarations (we can introduce another property to only get the non-interleaved ones)
  • rule.style.setProperty() (and accessors) adds to the last interleaved CSSStyleDeclaration (if any are present). The third argument is turned into a dictionary with a priority property and another property (name TBD) to reverse this behavior and actually add it to the first block of declarations.

Pros & Cons:

  • 👍🏼 Max compatibility with existing code
  • 👍🏼 Fewer footguns for authors (minimize bugs if authors forget to handle the case of interleaved declarations)
  • 👍🏼 More sensible API for when inline styles also support nesting.
  • 👀 Would this work or would it end up not predicting author intent well? What cases am I missing?

If we decide to avoid magic here, we can make the API more palatable by:

  • Introducing a new rule property that returns a CSSStyleDeclaration for the union of rule.style and interleaved declarations
  • Introducing a rule.setProperty() method that would add the property at the end of the last interleaved declaration. rule.style.setProperty() would continue to do what it currently does. Same for removeProperty(), getPropertyValue() etc.
@romainmenke
Copy link
Member

romainmenke commented Apr 20, 2024

Thank you @LeaVerou.

As a design principle, I don't think CSS should have author-facing syntax that provides no benefit to authors. Either we should come up with actual use cases for @nest

I actually want the @nest rule but as a more generic organizational tool.

In JavaScript it is possible to write blocks without any condition. I use this very frequently to group bits of code in a larger section.

This new at-rule could serve a similar purpose. Most authors will never use it but they could to organise and structure their code.

Today we use either multiple rules with the same selector, or comments when we want to group lists of declarations. Neither is really good.

This is our list of custom props when we start a new project :

:root {
	/* #region Colors */
	--color-foreground: rgb(0 0 0);
	--color-background: rgb(255 255 255);
	--color-grey-40: rgb(108 112 108);
	--color-warning: rgb(242 149 0);
	--color-error: rgb(207 0 0);
	/* #endregion Colors */

	/* #region Typography */
	--font-sans: "Helvetica Neue", sans-serif;
	--font-serif: "Georgia", serif;
	/* #endregion Typography */

	/* #region Spacers */
	--space-1--px: 1px;
	--space-1--rem: 0.0625rem;
	--space-8--px: 8px;
	--space-8--rem: 0.5rem;
	--space-16--px: 16px;
	--space-16--rem: 1rem;
	--space-32--px: 32px;
	--space-32--rem: 2rem;
	--space-48--px: 48px;
	--space-48--rem: 3rem;
	/* #endregion Spacers */
}

We need to add comments before and after each group so that we can have folding regions in code editors.

Screenshot of the source code above with the first group of custom properties collapsed

@tabatkins has also stated that they aren't attached to the name of this at-rule. Something that isn't tied to nesting would be nice.


If on the other hand, this can all be implementation details, then I prefer that.
I however fear that we can only kick this bucket further down the road and that some API will surface the warts.

@andruud
Copy link
Member

andruud commented Apr 20, 2024

In JavaScript it is possible to write blocks without any condition. I use this very frequently to group bits of code in a larger section.

@tabatkins has also stated that they aren't attached to the name of this at-rule. Something that isn't tied to nesting would be nice.

Do we need the @nest part of the rule? Can we use naked {}? Looking at css-syntax, I don't immediately see why not. In other words:

:root {
  /* #region Colors */
  {
    --color-foreground: rgb(0 0 0);
    --color-background: rgb(255 255 255);
    --color-grey-40: rgb(108 112 108);
    --color-warning: rgb(242 149 0);
    --color-error: rgb(207 0 0);
  }

  /* ... etc ... */
}

fixing the specifics is more web compatible than changing the current behavior after even longer.

To be clear, this issue blocks actually carrying out #8738. The more we mess around with the details here, the higher the risk that no change will happen at all.

@LeaVerou
Copy link
Member Author

To be clear, this issue blocks actually carrying out #8738. The more we mess around with the details here, the higher the risk that no change will happen at all.

That was not the resolution at all. Please implement the @nest rule as currently specced by @tabatkins. It is far, far easier to change the details of that than to change the current behavior of nesting later. That's the whole point of the resolution we made on Wednesday!

@andruud
Copy link
Member

andruud commented Apr 22, 2024

@LeaVerou Oh. I thought the point of the resolution was to focus the discussion and hopefully converge on something.

Unfortunately I can't make a move on @nest until this issue is resolved. I will likely not be able to ship a breaking change with the intent to break it again later, especially when there's no consensus on what the follow-up change would be.

It is far, far easier to change the details of that than to change the current behavior of nesting later.

The specific changes we're discussing here can not necessarily be made later at all. It is sometimes very hard to understand the risk of changing CSSOM APIs.

@Loirooriol
Copy link
Contributor

@andruud It's not with the "intent to break it again later". It's with the intent to keep @nest as a better option than the "shifting up", but leaving the door open to explore other possibilities.

The specific changes we're discussing here can not necessarily be made later at all.

If later on we prefer to switch to something else but it's not compatible, then so be it, we can stay with @nest.

The point is that there was consensus that @nest is better than "shifting up". Exploring other possible approaches shouldn't block @nest, since delaying it will just make it not compatible.

@tabatkins
Copy link
Member

As I said in my final comment in 8738, when considering any new feature, or additional complexity in an existing feature, it's worth paying attention to both the size of the affected audience and the size of the benefit that audience will receive.

The existence of an @nest rule is visible solely to authors who are crawling the CSSOM, or serializing the OM and then looking at it. Both of these are advanced use-cases, they're not something that 99% (and I think that's a very conservative percentage) of authors will ever do. I, myself, have never inspected the CSSOM in my entire webdev career except when writing WPTs.

So, the audience of people affected by any change here is very tiny, I'll argue, and that audience is composed of relatively skilled authors. (I think this audience can be reasonably summarized as "people who are writing CSS tooling".)

Do any of these changes allow that audience to do something currently difficult or impossible? No, these are solely attempting to improve the consistency of how the OM is represented in certain aspects.

Do they improve this audience's experience in a meaningful way? Arguable. While they improve the consistency in some ways (making the OM and/or the reserialized string more closely resemble the originally authored code), they reduce consistency in other ways (unusual serialization rules; edge cases that require restrictions on where rules can be moved around; widening of the possible types in certain attributes).

For example, right now you can, in theory, take any of the objects in an MQ's .cssRules list and move it out of that MQ, appending it to the stylesheet itself. If the object here is a CSSStyleDeclaration, what happens? Is that an error? If it's a CSSNestRule, but we have special serialization rules that make it look like raw declarations, how does it serialize in this case? (Or, again, maybe it causes an error?)

Or take .insertRule(). Its first argument is a string, which is parsed to obtain a rule to insert. (You can pass a rule object directly, but it's serialized and then re-parsed.) If you can't write @nest directly in a stylesheet, how do you insert such a rule? Do we have to now accept code like x.insertRule("color: blue;"), which is invalid today?

Or a parsed stylesheet will never have an @nest at the start of a style rule, or next to each other. But via CSSOM manipulation you can construct such situations. How do they serialize? Will you still end up with a stylesheet that parses into a different OM than it started from

Whatever we do, if it's "magic" in some way, it'll spawn corner cases like this. Magic like this can be justified, by sufficient benefit to a sufficient audience. I don't think that it qualifies on either metric in this case, however.

In summary, we should just do the simplest possible thing, with the least amount of magic possible, because all of the suggested magic (beyond the initial parser magic) won't actually pay for itself.

@tabatkins
Copy link
Member

Do we need the @nest part of the rule? Can we use naked {}? Looking at css-syntax, I don't immediately see why not.

We could, it would just be a new syntax construct to define. Relying on the existing at-rule syntax just makes for a slightly simpler model. And since we don't anticipate authors actually writing this themselves, I don't think the length of the name actually matter in any meaningful way.

@LeaVerou
Copy link
Member Author

@andruud

@LeaVerou Oh. I thought the point of the resolution was to focus the discussion and hopefully converge on something.

Unfortunately I can't make a move on @nest until this issue is resolved. I will likely not be able to ship a breaking change with the intent to break it again later, especially when there's no consensus on what the follow-up change would be.

It is far, far easier to change the details of that than to change the current behavior of nesting later.

The specific changes we're discussing here can not necessarily be made later at all. It is sometimes very hard to understand the risk of changing CSSOM APIs.

We are trying to avoid a breaking change by shipping @nest in some shape or form. @tabatkins is arguing right under your comment that how we implement this in the OM only affects a tiny fraction of authors. You are saying that it would be a breaking change and that "it’s very hard to understand the risk of changing CSSOM APIs". It may be productive if you two have a breakout to align on this, since you work at the same company? Because unless I’m missing something, I don't see how both statements can be true (it is both impactful and not impactful).

@tabatkins

As I said in my final comment in 8738, when considering any new feature, or additional complexity in an existing feature, it's worth paying attention to both the size of the affected audience and the size of the benefit that audience will receive.

Tab, nobody is disagreeing that I/E matters, we’re disagreeing on what the I is.

The existence of an @nest rule is visible solely to authors who are crawling the CSSOM, or serializing the OM and then looking at it. Both of these are advanced use-cases, they're not something that 99% (and I think that's a very conservative percentage) of authors will ever do. I, myself, have never inspected the CSSOM in my entire webdev career except when writing WPTs.

I have responded to this point in the OP:

I strongly disagree with Tab that reading and modifying the CSS OM only affects about a hundred developers in the entire world (!). Reading and modifying the CSS OM is at the core of a ton of very widespread libraries (e.g. jQuery, D3, etc). Even if most authors do not use it directly, I assure you almost every developer reads and modifies the CSS OM regularly, it's just done through several layers of abstractions.

Also, since we’re sharing anecdotes, I have crawled and modified the CSS OM dozens of times in my web dev career, none of which was while writing WPTs. I did not mention it earlier because as an anecdote, I didn’t think it was that relevant.

So, the audience of people affected by any change here is very tiny, I'll argue, and that audience is composed of relatively skilled authors. (I think this audience can be reasonably summarized as "people who are writing CSS tooling".)

Again, the users of these tools are not a small audience.

For example, right now you can, in theory, take any of the objects in an MQ's .cssRules list and move it out of that MQ, appending it to the stylesheet itself. If the object here is a CSSStyleDeclaration, what happens? Is that an error? If it's a CSSNestRule, but we have special serialization rules that make it look like raw declarations, how does it serialize in this case? (Or, again, maybe it causes an error?)

I don’t see how this is different than defining what @nest {} does in the root scope. We’ll have to define something however you look at it. And if anything, defining what root-level declarations do is actually quite useful (anecdotally, I’ve written special code to handle raw declarations at the root level several times when coding CSS demos).

Actually, depending on how we define it root-level declarations could even solve all these use cases that require setting stuff on the document node.

Or take .insertRule(). Its first argument is a string, which is parsed to obtain a rule to insert. (You can pass a rule object directly, but it's serialized and then re-parsed.) If you can't write @nest directly in a stylesheet, how do you insert such a rule? Do we have to now accept code like x.insertRule("color: blue;"), which is invalid today?

Yes! Which again seems like a win and more broadly useful.

Or a parsed stylesheet will never have an @nest at the start of a style rule, or next to each other. But via CSSOM manipulation you can construct such situations. How do they serialize? Will you still end up with a stylesheet that parses into a different OM than it started from

I’m not married to the details, but I was envisioning that these nodes would be merged automatically by the CSS OM, not during serialization. All the Web Platform’s OMs are full of these kinds of normalizations, so I don’t see what the problem is.

@tabatkins
Copy link
Member

The users of tools that crawl/modify the CSSOM only matter for this proposal insofar as those tools expose the CSSOM to them (or this proposal makes those tools work better, thus improving the experience of their users). Do you have examples of that?

I don’t see how this is different than defining what @nest {} does in the root scope.

Sure, we have to define what @nest does in the global scope, in exactly the same way we defined what & matches in the global scope. (And it's currently defined to act exactly the same.) But these additional things are more corner cases to define.

Yes! Which again seems like a win and more broadly useful.

How is this a meaningful win over x.insertRule("@nest { color: blue; }")? I mean, it's very slightly shorter, sure. But does it matter otherwise? Is it worth the consistency loss of insertRule() no longer being restricted to taking rules?

I’m not married to the details, but I was envisioning that these nodes would be merged automatically by the CSS OM, not during serialization. All the Web Platform’s OMs are full of these kinds of normalizations, so I don’t see what the problem is.

I don't think that's accurate to say. For example, in the following:

<!DOCTYPE html>
<p>
<script>
var p = document.querySelector("p");
p.appendChild(new Text("foo"));
p.appendChild(new Text("bar"));
console.log(p.childNodes);
</script>

You'll get two Text nodes, despite that being essentially identical to just having a single Text node with "foobar". Having nodes magically merge underneath you as a side-effect of doing something unrelated (like removing a rule that previously happened to separate two of them) is the sort of thing that causes bugs in author code very easily, since objects disappear, indexes change, etc. in an unpredictable manner.

@andruud
Copy link
Member

andruud commented Apr 23, 2024

Do we need the @nest part of the rule? Can we use naked {}? Looking at css-syntax, I don't immediately see why not.

We could, it would just be a new syntax construct to define. Relying on the existing at-rule syntax just makes for a slightly simpler model. And since we don't anticipate authors actually writing this themselves, I don't think the length of the name actually matter in any meaningful way.

Yeah, sure. {} was a response to @romainmenke's world where authors might actually want to write it themselves. If @nest{} basically does nothing (except add structure), perhaps {} is a better way to represent it (and more in line with other languages).

Because unless I’m missing something, I don't see how both statements can be true (it is both impactful and not impactful).

That is not what I'm saying. I'm saying it's not always easy to prove that CSSOM changes won't break sites, even if you strongly believe it won't.


But OK, if shipping @nest ASAP is indeed what everyone wants here, I can try. I'll need positions from Apple and Mozilla. (EDIT: mozilla/standards-positions#1013, WebKit/standards-positions#337).

@LeaVerou
Copy link
Member Author

If we have author facing syntax for this, I'd prefer it's not {} so we can reserve {} for something that is actually useful (of course this changes if we can figure out a way to make @nest {} useful).


But OK, if shipping @nest ASAP is indeed what everyone wants here, I can try. I'll need positions from Apple and Mozilla. (EDIT: mozilla/standards-positions#1013, WebKit/standards-positions#337).

Thank you!

@dbaron
Copy link
Member

dbaron commented Apr 24, 2024

One question that would affect my opinion of whether the CSSOM questions here matter: what's the chance that at some point in the future we'll allow the nesting syntax inside the style attribute? (This sort of syntax has been proposed and specified in the past, but it didn't get traction at the time.) I think the CSSOM is much more relevant for the style attribute than it is for style sheets, since it's more widely used there.

@cdoublev
Copy link
Collaborator

  1. No @nest, just CSSStyleDeclaration in the CSSOM
    • Criticism: That means .cssRules will also return non-rules? Would .insertRule() also accept CSSStyleDeclaration?

I am not sure I understood the whole conversation between mdubet and emilio about this in #8738, but intermixing list of declarations (or CSSStyleDeclarations) and rules in specified order in an internal property (eg. contents) seemed like an idiomatic solution.

The first list of declarations in contents should be exposed by style. .cssRules and .insertRule() should ignore lists of declarations in contents. .deleteRule() should eventually move declarations into the first list. It does not seem that painfull.

I did not understood why contents would need to be exposed by the public .cssRules. I guess in order to add declarations after rules via the CSSOM. Assuming there is a need for this, a new interface like .insertDeclaration() and/or styles could be defined.

But accepting declarations in .insertRule(), ugh. 😬

@LeaVerou
Copy link
Member Author

LeaVerou commented Apr 24, 2024

One question that would affect my opinion of whether the CSSOM questions here matter: what's the chance that at some point in the future we'll allow the nesting syntax inside the style attribute? (This sort of syntax has been proposed and specified in the past, but it didn't get traction at the time.) I think the CSSOM is much more relevant for the style attribute than it is for style sheets, since it's more widely used there.

My understanding is that this is basically planned, and it's just a matter of resource allocation. Inline style nesting is a huge use case.


Since it seems hard to get consensus on making this rule less author facing, could we explore going the other way? I.e. keeping is as author-facing, and trying to make it useful in some way.

Some ideas:

  • Someone mentioned that it could be used for grouping. That definitely makes sense. Other threads discuss a potential @sheet rule that basically does named grouping. I wonder if these two could be combined.
  • Keeping fallbacks around that can be used for IACVT
  • Simple style() conditionals on the current element? Or some other way to toggle these groups on or off or pick among two?

Just brainstorming here. What would be some low hanging, easy-ish to implement functionality that could make this rule actually useful?

Perhaps we should simply name it @group and allow an optional <dashed-ident> before the {, then we can figure out later what these can be used for.

@emilio
Copy link
Collaborator

emilio commented Apr 24, 2024

I did not understood why contents would need to be exposed by the public .cssRules. I guess in order to add declarations after rules via the CSSOM. Assuming there is a need for this, a new interface like .insertDeclaration() and/or styles could be defined.

You really want authors to be able to see all the styles in the stylesheet, in the right order. If cssRules doesn't do that you need something else.

@emilio
Copy link
Collaborator

emilio commented Apr 24, 2024

Another thing to note, not sure how well this interacts with @scope... @andruud / @dshin-moz / @tabatkins / @mirisuzanne, do you know off-hand?

IIRC we either stash an implicit :scope in the selector (but we can't with @nest, because there's no selector), or use the & which now includes the start bound, right? At least I recall a weird interaction between nesting and @scope, but I don't recall the specifics...

That is, what does:

#a {
  ...

  @scope (#b) {
    color: red;
  }

desugar to now? What would desugar to with @nest and would it work correctly?

@mdubet
Copy link

mdubet commented Apr 24, 2024

@emilio Nothing different for @scope than the other nested group rules : we currently wrap the declarations in & which desugar to :is(#b) (because the <scope-start> acts as a parent selector).

Exploring the use cases for a more general @group rule is a good path forward (even just changing the name to anything less specific than @nest is a progression imho)

@emilio
Copy link
Collaborator

emilio commented Apr 25, 2024

Oh, so the fact that the scope rule is inside the #a selector is completely lost already? That's a bit weird / unintuitive but ok I guess

@andruud
Copy link
Member

andruud commented Apr 25, 2024

completely lost

Maybe it's not what you meant, but it's not completely lost, it's in the implicit & in the <scope-start>:

#a {
  ...
  @scope (& #b) {
    @nest {
      color: red;
    }
  }

I think that will be just fine.


Perhaps we should simply name it @group and allow an optional <dashed-ident> before the {, then we can figure out later what these can be used for.

I support this too.

Although we could easily add a <dashed-ident> to the prelude later, if/when we have a good reason for it. Maybe we just ship @group {} for now, leaving the prelude wide open for whatever we come up with later.

@emilio
Copy link
Collaborator

emilio commented Apr 25, 2024

Ah, cool, It's added to the start of the scope, that seems fine then I agree.

I don't mind calling it @group (but if we do so then it probably should be a grouping rule after all, and not be restricted to declarations...)

@andruud
Copy link
Member

andruud commented Apr 25, 2024

I don't mind calling it @group (but if we do so then it probably should be a grouping rule after all, and not be restricted to declarations...)

Yes, agree.

@romainmenke
Copy link
Member

So authors could (but likely won't) write :

:root {
  @group {
    --color-a: pink;

    @media screen { --color-a: red; }

    --color-b: lime;
  }
}

Which would be equivalent to :

:root {
  @group {
    --color-a: pink;

    @media screen { --color-a: red; }

    @group { --color-b: lime; }
  }
}

@LeaVerou
Copy link
Member Author

Although we could easily add a <dashed-ident> to the prelude later, if/when we have a good reason for it. Maybe we just ship @group {} for now, leaving the prelude wide open for whatever we come up with later.

The thinking was to support this kind of usage.

It would be nice though if we could come up with some actual useful functionality for these groups other than simple grouping. Are any of the things I mentioned here feasible?

@andruud
Copy link
Member

andruud commented Apr 26, 2024

To be clear, this issue blocks actually carrying out #8738. The more we mess around with the details here, the higher the risk that no change will happen at all.

That was not the resolution at all. Please implement the @nest rule as currently specced by @tabatkins. It is far, far easier to change the details of that than to change the current behavior of nesting later. That's the whole point of the resolution we made on Wednesday!

With WebKit's position now clarified, and after some feedback on the Blink Intent-to-Ship, it's pretty clear that this issue is in fact blocking #8738.

@mirisuzanne
Copy link
Contributor

This is a long-shot, not well thought-through - but is there a way for named-group declarations to be available as a form of name-spaced custom property. I don't think this would provide actual new functionality, but some syntax shortcuts for name-spacing and declaring a custom/standard property at the same time:

.now {
  --base-padding: 1em;
  padding: var(--base-padding);

  --color-primary: teal;
  --color-secondary: hotPink;
}

.groups {
  @group --base {
    padding: 1em;
  }

  @group --color {
    --primary: teal;
    --secondary: hotPink;    
  }
  
  .access {
    margin: var(--base.padding);
    border-color: var(--color.--primary);
  }
}

(Hope I'm not straying too far from the question at hand - but it seems like an otherwise-useful rule might help with WebKit's concerns?)

@LeaVerou
Copy link
Member Author

LeaVerou commented Jun 10, 2024

Possibly too late to change the design, but I just had an idea: What if we do introduce the rule, call it @group, and instead of introducing identifiers in the preamble, we have a revert-group value that would work similarly to revert-layer, i.e. would revert the declaration to what it would have been if the group was not applied.
Then groups have a purpose that is not turned on by default (making them suitable for representing nested declarations) but is still super useful: they allow authors to override the IACVT behavior with an actual fallback! This is especially useful in combination with something like if():

@group {
	border-radius: if(style(--button-shape: pill), infinity, revert-group);
}

nex3 added a commit to sass/sass that referenced this issue Jul 4, 2024
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 22, 2024
The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516
[1] w3c/csswg-drafts#10234
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 27, 2024
The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
[1] w3c/csswg-drafts#10234
aarongable pushed a commit to chromium/chromium that referenced this issue Aug 27, 2024
The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 27, 2024
The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 27, 2024
The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Aug 28, 2024
…tions behind a flag, a=testonly

Automatic update from web-platform-tests
[css-nesting] Implement CSSNestedDeclarations behind a flag

The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}

--

wpt-commits: b23720e948f2af9d5ddb01d94ca49fbd1c40382e
wpt-pr: 47744
ErichDonGubler pushed a commit to erichdongubler-mozilla/firefox that referenced this issue Aug 29, 2024
…tions behind a flag, a=testonly

Automatic update from web-platform-tests
[css-nesting] Implement CSSNestedDeclarations behind a flag

The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}

--

wpt-commits: b23720e948f2af9d5ddb01d94ca49fbd1c40382e
wpt-pr: 47744
i3roly pushed a commit to i3roly/firefox-dynasty that referenced this issue Aug 29, 2024
…tions behind a flag, a=testonly

Automatic update from web-platform-tests
[css-nesting] Implement CSSNestedDeclarations behind a flag

The CSSWG recently resolved to change how "bare" declarations work
when mixed with nested style rules. Previously, any declaration
following a nested style rule would be "shifted up" to join
the leading declarations, but as of the work leading up to
Issue 10234 [1], such declarations now instead remain in place
(wrapped in a CSSNestedDeclarations rule).

This CL implements this rule, as well as the parser changes needed
to produce those rules during ConsumeDeclarationList.
The parsing behavior is similar to the behavior previously seen
for emitting signalling/invisible rules (removed in CL:5593832),
although naturally without any signalling, nor any invisibility.

Per spec, CSSNestedDeclarations is a kind of style rule that matches
exactly what its parent style rule matches, and with the same
specificity behavior. This is different from the '&' pseudo-class,
which uses the maximum specificity across its arguments, and can't
match pseudo-elements. This CL implements this via an inner
StyleRule, held by the CSSNestedDeclarations rule.
This inner StyleRule can't be observed via CSSOM. It exists primarily
to be able to bucket the rule normally on RuleSet.

[1] w3c/csswg-drafts#10234

Change-Id: If9afe0cbb41e7de0acdd781ecfbf6884d677c6f8
Binary-Size: crbug.com/344608183
Bug: 343463516, 361600667
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5594117
Reviewed-by: Steinar H Gunderson <[email protected]>
Commit-Queue: Anders Hartvoll Ruud <[email protected]>
Reviewed-by: Philip Pfaffe <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1347266}

--

wpt-commits: b23720e948f2af9d5ddb01d94ca49fbd1c40382e
wpt-pr: 47744
mdubet added a commit to mdubet/WebKit that referenced this issue Aug 31, 2024
…nd rules

https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by NOBODY (OOPS!).

Previously, any bare declaration inside a style rule, whatever its position
relative to other rules, would be shifted up at the top of the rule
to be able to represent a style rule with a single continous leading block of declarations.

This behavior has been fixed so the order of the declarations is respected
during cascade. w3c/csswg-drafts#10234

This introduces a new CSSNestedDeclarations object (a new kind of CSS style rule) to be able to store those block of declarations in-between rules and fit with the already existing RuleData/RuleSet mechanism. It is purposely not exposed as a style rule in the CSSOM.

The CSSOM insertRule() functions (on CSSStyleRule and on CSSGroupingRule) is modified to allow inserting block of declarations. w3c/csswg-drafts#10520

https://drafts.csswg.org/css-nesting/#the-cssnestrule

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:

  Manual sync from WPT

* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:
mdubet added a commit to mdubet/WebKit that referenced this issue Sep 3, 2024
…nd rules

https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by NOBODY (OOPS!).

Previously, any bare declaration inside a style rule, whatever its position
relative to other rules, would be shifted up at the top of the rule
to be able to represent a style rule with a single continous leading block of declarations.

This behavior has been fixed so the order of the declarations is respected
during cascade. w3c/csswg-drafts#10234

This introduces a new CSSNestedDeclarations object (a new kind of CSS style rule) to be able to store those block of declarations in-between rules and fit with the already existing RuleData/RuleSet mechanism. It is purposely not exposed as a style rule in the CSSOM.

The CSSOM insertRule() functions (on CSSStyleRule and on CSSGroupingRule) is modified to allow inserting block of declarations. w3c/csswg-drafts#10520

https://drafts.csswg.org/css-nesting/#the-cssnestrule

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:

  Manual sync from WPT

* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:
mdubet added a commit to mdubet/WebKit that referenced this issue Sep 3, 2024
…nd rules

https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by NOBODY (OOPS!).

Previously, any bare declaration inside a style rule, whatever its position
relative to other rules, would be shifted up at the top of the rule
to be able to represent a style rule with a single continous leading block of declarations.

This behavior has been fixed so the order of the declarations is respected
during cascade. w3c/csswg-drafts#10234

This introduces a new CSSNestedDeclarations object (a new kind of CSS style rule) to be able to store those block of declarations in-between rules and fit with the already existing RuleData/RuleSet mechanism. It is purposely not exposed as a style rule in the CSSOM.

The CSSOM insertRule() functions (on CSSStyleRule and on CSSGroupingRule) is modified to allow inserting block of declarations. w3c/csswg-drafts#10520

https://drafts.csswg.org/css-nesting/#the-cssnestrule

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:

  Manual sync from WPT

* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:
mdubet added a commit to mdubet/WebKit that referenced this issue Sep 4, 2024
…nd rules

https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by NOBODY (OOPS!).

Previously, any bare declaration inside a style rule, whatever its position
relative to other rules, would be shifted up at the top of the rule
to be able to represent a style rule with a single continous leading block of declarations.

This behavior has been fixed so the order of the declarations is respected
during cascade. w3c/csswg-drafts#10234

This introduces a new CSSNestedDeclarations object (a new kind of CSS style rule) to be able to store those block of declarations in-between rules and fit with the already existing RuleData/RuleSet mechanism. It is purposely not exposed as a style rule in the CSSOM.

The CSSOM insertRule() functions (on CSSStyleRule and on CSSGroupingRule) is modified to allow inserting block of declarations. w3c/csswg-drafts#10520

https://drafts.csswg.org/css-nesting/#the-cssnestrule

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:

  Manual sync from WPT

* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:
mdubet added a commit to mdubet/WebKit that referenced this issue Sep 5, 2024
https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by NOBODY (OOPS!).

w3c/csswg-drafts#10234

Before this CSSWG resolution, any declaration inside a style rule
(whatever its position relative to other rules),
would be shifted up at the top of the rule
to be able to represent a style rule with a single leading block of declarations.
This patch implements the new resolved behavior so the order of interleaved declarations is respected
during cascade.

https://drafts.csswg.org/css-nesting/#the-cssnestrule

This patch introduces a new StyleRuleNestedDeclarations class
to be able to store a block of declarations in-between rules and fit with the already existing
RuleData/RuleSet mechanism.
Its CSSOM representation (CSSNestedDeclarations) is purposedly not serialized
as a rule in the CSSOM but like a list of declarations.

The CSSOM insertRule() functions (on CSSStyleRule/CSSGroupingRule)
are modified to allow inserting block of declarations.
w3c/csswg-drafts#10520

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:  Manual sync from WPT
* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:
webkit-commit-queue pushed a commit to mdubet/WebKit that referenced this issue Sep 5, 2024
https://bugs.webkit.org/show_bug.cgi?id=275365
rdar://130094168

Reviewed by Tim Nguyen.

w3c/csswg-drafts#10234

Before this CSSWG resolution, any declaration inside a style rule
(whatever its position relative to other rules),
would be shifted up at the top of the rule
to be able to represent a style rule with a single leading block of declarations.
This patch implements the new resolved behavior so the order of interleaved declarations is respected
during cascade.

https://drafts.csswg.org/css-nesting/#the-cssnestrule

This patch introduces a new StyleRuleNestedDeclarations class
to be able to store a block of declarations in-between rules and fit with the already existing
RuleData/RuleSet mechanism.
Its CSSOM representation (CSSNestedDeclarations) is purposedly not serialized
as a rule in the CSSOM but like a list of declarations.

The CSSOM insertRule() functions (on CSSStyleRule/CSSGroupingRule)
are modified to allow inserting block of declarations.
w3c/csswg-drafts#10520

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html:  Manual sync from WPT
* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
* Source/WebCore/css/CSSGradientValue.h:
* Source/WebCore/css/CSSNestedDeclarations.cpp: Added.
(WebCore::CSSNestedDeclarations::CSSNestedDeclarations):
(WebCore::CSSNestedDeclarations::style):
(WebCore::CSSNestedDeclarations::cssText const):
(WebCore::CSSNestedDeclarations::reattach):
* Source/WebCore/css/CSSNestedDeclarations.h: Added.
* Source/WebCore/css/CSSNestedDeclarations.idl: Added.
* Source/WebCore/css/CSSShapeSegmentValue.cpp:
* Source/WebCore/css/CSSStyleRule.cpp:
(WebCore::CSSStyleRule::insertRule):
* Source/WebCore/css/CSSStyleRule.h:
* Source/WebCore/css/StyleRule.cpp:
(WebCore::StyleRuleBase::visitDerived):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
(WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations):
(WebCore::StyleRuleNestedDeclarations::debugDescription const):
* Source/WebCore/css/StyleRule.h:
(WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const):
(isType):
* Source/WebCore/css/StyleRuleType.h:
* Source/WebCore/css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::traverseRules const):
(WebCore::StyleSheetContents::traverseSubresources const):
(WebCore::StyleSheetContents::mayDependOnBaseURL const):
* Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp:
* Source/WebCore/css/calc/CSSCalcTree+Simplification.h:
* Source/WebCore/css/parser/CSSParser.h:
* Source/WebCore/css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::parseNestedDeclarations):
(WebCore::CSSParserImpl::createNestedDeclarationsRule):
(WebCore::CSSParserImpl::consumeNestedGroupRules):
(WebCore::CSSParserImpl::consumeBlockContent):
(WebCore::CSSParserImpl::createNestingParentRule): Deleted.
* Source/WebCore/css/parser/CSSParserImpl.h:
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp:
(WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction):
* Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h:
* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(WebCore::flatteningStrategyForStyleRuleType):
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::addChildRule):
(WebCore::Style::RuleSetBuilder::addStyleRule):
* Source/WebCore/style/RuleSetBuilder.h:

Canonical link: https://commits.webkit.org/283188@main
@backtracking-ali
Copy link

Thank you @LeaVerou.

As a design principle, I don't think CSS should have author-facing syntax that provides no benefit to authors. Either we should come up with actual use cases for @nest

I actually want the @nest rule but as a more generic organizational tool.

In JavaScript it is possible to write blocks without any condition. I use this very frequently to group bits of code in a larger section.

This new at-rule could serve a similar purpose. Most authors will never use it but they could to organise and structure their code.

Today we use either multiple rules with the same selector, or comments when we want to group lists of declarations. Neither is really good.

This is our list of custom props when we start a new project :

:root {
/* #region Colors /
--color-foreground: rgb(0 0 0);
--color-background: rgb(255 255 255);
--color-grey-40: rgb(108 112 108);
--color-warning: rgb(242 149 0);
--color-error: rgb(207 0 0);
/
#endregion Colors */

/* #region Typography /
--font-sans: "Helvetica Neue", sans-serif;
--font-serif: "Georgia", serif;
/
#endregion Typography */

/* #region Spacers /
--space-1--px: 1px;
--space-1--rem: 0.0625rem;
--space-8--px: 8px;
--space-8--rem: 0.5rem;
--space-16--px: 16px;
--space-16--rem: 1rem;
--space-32--px: 32px;
--space-32--rem: 2rem;
--space-48--px: 48px;
--space-48--rem: 3rem;
/
#endregion Spacers */
}
We need to add comments before and after each group so that we can have folding regions in code editors.

Screenshot of the source code above with the first group of custom properties collapsed [@tabatkins](https://github.com/tabatkins) has also stated that they aren't attached to the name of this at-rule. Something that isn't tied to nesting would be nice.

If on the other hand, this can all be implementation details, then I prefer that. I however fear that we can only kick this bucket further down the road and that some API will surface the warts.

https://javascripter.space/questions/can-you-reference-documentstylesheetscssrules-by-specifying-a-rule-name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-nesting-1 Current Work
Projects
None yet
Development

Successfully merging a pull request may close this issue.