https://drafts.csswg.org/css-nesting/#at-nest
The @nest rule functions identically to a style rule: it starts with a selector, and contains declarations that apply to the elements the selector matches. The only difference is that the selector used in a @nest rule must be nest-containing, which means it contains a nesting selector in it somewhere. A list of selectors is nest-containing if all of its individual complex selectors are nest-containing.
Since there is no ambiguity with @nest, neither syntactic, nor ambiguity of intent, why still force &? We can just default to the descendant selector, just like preprocessors.
This, is a little awkward:
.foo {
@nest & .bar {
color: white;
}
}
compared to:
.foo {
@nest .bar {
color: white;
}
}
Yes, in that case you don't actually need @nest, but some people may prefer it for the explicitness, or it could be generated. In any case, it's a sensible default, that is trivial to implement, and has already been tested for both utility and usability in preprocessors.
I prefer the explicit syntax @nest & .bar. I think @nest .bar would cause confusion (did it mean .foo.bar, .foo > .bar or .foo .bar?)
I prefer the explicit syntax
@nest & .bar. I think@nest .barwould cause confusion (did it mean.foo.bar,.foo > .baror.foo .bar?)
That might have been a good argument if this was a theoretical discussion, but we've seen how this default nesting plays out in preprocessors and nobody gets confused.
Since one of the reasons for @nest is: "some people find the nesting challenging to distinguish visually from the surrounding declarations" when using direct nesting (meaning that for some people all nesting would be with the at rule), and @nest is supposed to impose "fewer restrictions on how to validly nest style rules", I agree that it should be possible to author without the nesting selector inside the @nest when there is no ambiguity.
I think @nest .bar should just mean .foo .bar. If you want .foo.bar or .foo > .bar, then you would use the nesting selector (@nest &.bar or @nest & > .bar). Even @nest > .bar (or @nest> .bar) should be allowed, I'd say, since that is also not ambiguous. A space (or lack thereof after @nest in places where spaces are optional without changing the meaning) should just represent a space after the parent.
nobody gets confused
Well, I have seen CSS newbies confused because <div><p><span> was affected by div span styles, turns out they thought it meant div > span. I suspect this may be happening in a non-negligible amount of cases, it's just that using the descendant combinator instead of the child one is usually harmless, so it goes unnoticed. I think that making the descendant combinator be a space (and thus the default one) was a mistake, and your proposal could produce similar confusions. I don't like having default combinators, since different people can have different intuitions about what the default should be. But well, maybe I should accept that it's a lost battle, your proposal is at least consistent with the current behavior.
Anyone could get confused, but I think simpleness and consistency with how existing selectors work, while retaining good parse-ability and lack of ambiguity are good goals.
I still prefer leaving the & requirement in there, for the same reasons I originally wrote the spec this way.
It keeps the value space more consistent between "direct" nesting and @nest nesting - the only change is the new ability introduced by @nest to put the "&" somewhere different. The less differences there are between the two cases, the more learnable it is.
Allowing a "missing &" to be inferred to be at the beginning introduces a "garden path" problem - you have to read the entire selector to know whether it's anchored at the beginning or somewhere in the middle, which changes the entire interpretation of the selector. Yes, existing preprocessors have bitten this bullet, but this is a real issue that PL designers take quite seriously - it shows up a lot in our JS syntax discussion in TC39. We shouldn't necessarily assume that preprocessors have made the most ideal choice here, as they didn't start out being written by people with PL design experience, and most later ones have just followed along.
Related to the above, more than one & is allowed in a nesting selector, so the presence of an & later in the selector does not indicate that the selector shouldn't also start with an &! This is why garden-path problems are insidious, as they indicate non-local influences; an author can start with a perfectly reasonable selector with no & at all, relying on the implicit & at the start, then later use an & inside of a pseudo-class and suddenly have the selector drastically change its interpretation. (For example, changing @nest .foo .bar {...} to @nest .foo .bar:not(&) {...}.) One could try to come up with rules to handle this, but I assure you they won't be complete, and they will drastically complexify the syntax surface here, which is a huge code smell.
Defaulting to a descendant selector, as preprocessors do, isn't the best thing perf-wise. I'd still rather have authors intentionally use the descendant selector than opt them into it by default because it's the shortest way to write something.
So for all these reasons, I'm still strongly in favor of keeping the existing syntax requirement. It's literally two characters, in return for a much simpler and more predictable syntax surface.
Fair enough, these are really good points.
Most helpful comment
I still prefer leaving the
&requirement in there, for the same reasons I originally wrote the spec this way.It keeps the value space more consistent between "direct" nesting and
@nestnesting - the only change is the new ability introduced by@nestto put the "&" somewhere different. The less differences there are between the two cases, the more learnable it is.Allowing a "missing &" to be inferred to be at the beginning introduces a "garden path" problem - you have to read the entire selector to know whether it's anchored at the beginning or somewhere in the middle, which changes the entire interpretation of the selector. Yes, existing preprocessors have bitten this bullet, but this is a real issue that PL designers take quite seriously - it shows up a lot in our JS syntax discussion in TC39. We shouldn't necessarily assume that preprocessors have made the most ideal choice here, as they didn't start out being written by people with PL design experience, and most later ones have just followed along.
Related to the above, more than one
&is allowed in a nesting selector, so the presence of an&later in the selector does not indicate that the selector shouldn't also start with an&! This is why garden-path problems are insidious, as they indicate non-local influences; an author can start with a perfectly reasonable selector with no&at all, relying on the implicit&at the start, then later use an&inside of a pseudo-class and suddenly have the selector drastically change its interpretation. (For example, changing@nest .foo .bar {...}to@nest .foo .bar:not(&) {...}.) One could try to come up with rules to handle this, but I assure you they won't be complete, and they will drastically complexify the syntax surface here, which is a huge code smell.Defaulting to a descendant selector, as preprocessors do, isn't the best thing perf-wise. I'd still rather have authors intentionally use the descendant selector than opt them into it by default because it's the shortest way to write something.
So for all these reasons, I'm still strongly in favor of keeping the existing syntax requirement. It's literally two characters, in return for a much simpler and more predictable syntax surface.