Closed fantasai closed 1 year ago
Also not a fan of a parsing switch.
This would require the author to keep track of which mode they are in: by looking at a nested selector they cannot know if they already are in one of both modes or not.
.foo {
/* insert 20 declarations here */
.bar & {} /* 🤔 Wait, which mode am I in right now? */
}
It also can become confusing, as moving blocks of code – e.g. a nested at-rule – can affect other pieces of unrelated code below it.
.foo {
/* insert 20 declarations here */
@media(…) {
…
}
.bar {} /* ❌ This line will suddenly break when you move/remove the nested at-rule included above it */
}
.foo {
…
& .foo {
…
}
.bar {} /* ❌ Moving this block a few lines upwards when reordering your code, requires the addition of an extra leading & in order to keep things working */
}
These last ones remind me of the “Two CSS properties walk into a bar” CSS joke, in a very negative way.
(@tabatkins) Note, tho, that this will slightly tie our hands in the future - we'll never be able to change the property syntax to start with a non-ident (like doing additive CSS by writing +transform: ... or something). This probably isn't a huge deal, but it is definitely a forwards-compat/language evolution issue to worry about.
Seems like a pretty significant limitation to introduce.
I've been very confused, like romainmenke, about some steps that were taken. The Syntax suggestion issue was promptly closed with a simple comment
Closing as resolved.
However it looks to be a completely open-ended thread and it is not at all clear how this resolution was reached, and more importantly, what the resolution actually was. And, when looking at this new thread, it doesn't seem like the syntax issue has been resolved even a little bit - it seems like we just somehow marked the previous issue as "resolved" just to continue the conversation here.
It would be great to get some clarification as to what happened so that people like myself can feel like they are up-to-date.
In the previous issue, I advocated for a syntax that simply required @nest;
before using nested rules. There were no additional requirements. In my opinion, it is absolutely vital that syntax is clear and consistent. The suggestions seem to be "well, you can just start nesting, unless your rule starts with a character that is not part of &
, or @
(some suggestions including all non-alphanumerical characters), then you either need to switch the order of the rules so that it does, or use an @nest;
". There are some JavaScript APIs that are in a similar manner dependent on context; e.g. document.all('foo')
could return an element with id="foo"
, but also an element with name="foo"
, and actually if there are multiple elements with name="foo"
on the page, it returns an HTMLCollection
with all of them. Of course, this is CSS, and so it is very different in nature than JavaScript, but I can't help noticing the similarities (and I'm sure most of us agree that the API design of document.all
was not good). I would think it's much safer, usable and future-proof if we go with something much more simple, like the required @nest;
.
Additionally, I think we're missing the point by trying to stay as close as possible to SCSS syntax. The way we're trying to do that is inevitably going to lead to situations where people expect a piece of SCSS to just work, but it doesn't. Minimizing the additional effort to convert a piece of SCSS to CSS is a valiant effort however but it does not mean we have to mimic SCSS's syntax. With the required @nest;
, authors would be aware this is necessary for their CSS to be valid. They can copy-paste SCSS, they will add the @nest;
if necessary, and there's no additional overhead of "oh, should I be changing stuff here? Do the rules start with the right characters, or should I change their order?".
Let me also bring up a previous point; the context awareness. Some people have mentioned that authors should be able to just see what context they are in (which is important if we introduce a parser switch). Now, overall, I don't think the parser mode will be difficult to recognize (and people won't even really need to know about the parser switch or what that even means), because a rule is a rule and a declaration is just that. We've already been doing it in SCSS and other preprocessors for a long time, proving this is not hard for authors to get used to. However, with the current discussion and suggestions in mind, if I see
.red { color: red; }
and change it to
h1 { color: black; }
.red { color: red; }
is this valid? Uh, well, that depends; what's above it? Conversely, if I see .red { color: red; }
, can I just remove that? Well, that also depends; if it has a rule in front of it, then yes, but if it's the first rule, then then next rule should... Etcetera. In terms of syntax it's a nightmare of mental overhead.
That seems like quite a problem. With something trivial like a required @nest;
, the answers to the questions are obvious. You need a @nest;
, so the simple addition of a rule like above is allowed regardless of context. There is a rule already, therefore there is an @nest;
, which means that yes, you're allowed to add a rule. If you see a rule like .red { color: red; }
, sure, remove it. Your CSS is guaranteed to still be valid.
Lastly, I'm going to repeat a point I mentioned in the previous thread. I mentioned then that
The current spec is carefully wiggling itself through the constrains that we have, and is doing so successfully, but the wiggling part of that is visible in the syntax, and anyone that's not familiar with or interested in parsers will probably be confused and annoyed with this syntax.
and I think we're merely amplifying this by throwing even more "if this, then..., otherwise..." in the mix.
To avoid confusion, I do not think a required @nest;
is "the" solution, but in my opinion, it ticks the most boxes of any suggestion so far. Specifically the simplicity and consistency that I believe is so important in language syntax is missing from many of the suggestions made in this thread.
I think @nest
-everywhere but with the @nest
auto-insertion rules so you rarely have to actually write it would work well in practice without the sharp edge of the mode-switch
@bramus The current evolution of the proposal would kick into rule parsing mode with .
as well, so nothing would break in your code examples if you move the @-rule.
@devongovett
I think & should be required in all nested selectors because it makes it clear where the parent selector is inserted and how the selectors are combined. Not requiring it leaves the reader guessing.
@romainmenke
My push back is because you can omit it in subsequent selectors, not because it is required in the first selectors.
The primary feedback we get from authors over and over is that they dislike that the &
is mandatory with the current syntax, and want a syntax that just assumes descendant if no &
is present. We are not trying to design a syntax closer to Sass because we believe that migration matters that much (as you point out it's a one time cost), but because if there were no parsing limitations, it appears that the Sass syntax has the best ergonomics.
If some authors think the &
should be mandatory always, in every rule, and some authors think it should be completely optional, I'm not sure how we will reach consensus, except by majority, which is not a good way to make decisions 😕
Though I will point out that if the &
is optional, nothing prevents you from having style guides that mandate it, so you can still write and read code the way you find preferable in your team, so in theory everyone can be happy. Making it mandatory means the % of authors that consider it noise to read and tedious to type cannot be happy.
I would just challenge whether that feedback is because people are familiar with SASS, or because it's intrinsically better/clearer. Disliking syntax is also different from not understanding it. People disliked the custom property syntax in the beginning as well.
There are a lot of comments about how “converting from XX framework is not a big problem”. Yeah, I agree; it's a one-time cost, you can run a script. That was not the point about conversion I opened this issue for; rather, it is specifically about copying code from one context to another in pure CSS. People write CSS within nested contexts and outside it, and within our future @scope
rules and outside it, and it is quite reasonable and common to copy code from one place to another. Copying code should work with minimal friction. Switching a block from nesting to scoping should work without friction. That's the issue.
Making & valid outside of nesting is great, we should do it, that's a separate issue, and it doesn't solve the problem that most unnested CSS code is written without it today and will likely continue to be written without it in the future.
As for the performance concerns of descendant selectors: authors want to use descendant selectors, and they will. I expect whether we require & here or not will hardly make a difference.
In the future, some large percentage of CSS rules will be nested. Whatever we do here, it needs to be ergonomic enough to be used for the majority of style rules in a website. And it needs to work naturally with @scope
.
Can we split up this proposal in multiple threads? Too much is being lost because everyone focuses on one ore two details of the previous statement. It is not easy to discuss the core of this proposal without getting stuck discussing the side effects.
I see these parts, but maybe there are more:
descendant operator as an implicit default when & is missing
is not in this list because it is a side effect of having relative selector syntax and a parser switch mechanic.
Things like the parser switch mechanic need to be researched in detail because it is something we can not take back, it will change the language fundamentally. A separate thread is more suitable in my opinion.
.bar & {} / 🤔 Wait, which mode am I in right now? /
To any CSS author that is clearly a rule, not a declaration, so I don't think that is confusing at all. We're saying now that non-idents, such a .
, #
, >
, +
, :
, &
, etc. would signify the beginning of a rule.
I agree with @LeaVerou and @fantasai that the reason we keep mentioning SASS (SCSS dialect) is not just author familiarity, but because it has good writing ergonomics, which complement how people author CSS. It makes it really simple to write nested descendant rules and rules that start with other combinators, and the &
character is easy to use for when you want to modify the subject selector in other ways (e.g. add a class to it, or prefix some other selectors to it). We should be aiming for something as close to this as we can, without boggling the parser or hurting parsing performance. Making everyone type a &
or @nest
before every selector is excessive, if we can avoid it. And we can.
I actually don't hate @LeaVerou's "crazy" idea. I could see that working. But I'd still rather just have that as the restriction on the first rule only, which is where we pretty much are at this point anyway. But it is a good framing: to disambiguate the beginning of nested rules from the declarations (which is the main reason to diverge from SCSS nesting at all), the first rule of the nest just can't start with a tag name selector. But it can start with a &
, and every other kind of selector is fine. That isn't an unreasonable thing to remember, even when moving rules around or deleting them. Kind of like how you can omit the last semicolon of a rule, but if you move that rule or add one after it, you'll need to remember to put that semicolon in there after all. In this case, you'll need to remember that the first rule of the nest has to start with something other than a tag name.
And if it helps your individual authoring style, precede all your tag-name-starting nested rules with a &
. For the minority of rules that need the &
elsewhere in the selector, and you don't have any other rules to put before it, I personally think preceding that selector (or the whole rule) with @nest
is a little easier to read that wrapping the tag name with :is()
but maybe that's just me. I could get used to either way.
.bar & {} / 🤔 Wait, which mode am I in right now? /
To any CSS author that is clearly a rule, not a declaration, so I don't think that is confusing at all. We're saying now that non-idents, such a
.
,#
,>
,+
,:
,&
, etc. would signify the beginning of a rule.
Thanks for clarifying (both you and @LeaVerou above).
Note, tho, that this will slightly tie our hands in the future - we'll never be able to change the property syntax to start with a non-ident (like doing additive CSS by writing +transform: ... or something). This probably isn't a huge deal, but it is definitely a forwards-compat/language evolution issue to worry about.
Seems like a pretty significant limitation to introduce.
It's probably not, I think. For one, we don't actually have any plans to do anything like that, so any concerns would be purely theoretical; this isn't a syntax space we've ever played in or planned to. For two, for the same reason I have to be careful in writing the syntax spec algo (to avoid accidentally switching into "rule mode" when someone just used an ascii char to "comment out" a property), we'd have to be extremely careful to avoid accidentally turning currently-invalid code into correct code with an unintended meaning. More than likely we'd just avoid this altogether and stick with putting new syntax between the property name and the colon instead, which is also compatible with this Nesting syntax change.
div {
background: green;
.background: red;
color: white;
}
With code like the above, the color
property still applies due to error recovery. But if .
started a different parsing mode, it wouldn't. Same with &
or any other currently-invalid character. It may be particularly common with *
as that was historically used as an IE-specific hack, but would also conflict with a selector.
Fortunately that's incorrect, due to how I proposed changing Syntax. Instead, that .
would cause us to attempt to "parse an ambiguous rule", and when we hit the ;
we'd realize it was instead an invalid property all along and just drop it, without changing the parsing mode. The color
property would then continue to be valid.
To organize the discussion a bit for tomorrow, the options we're looking at are:
&
or by being an @nest
rule. If not using @nest
, every selector in a list needs to start with &
, not just the first.@nest;
rule we'd introduce.&
. (Rules following the switch can start with whatever.).foo
, :hover
, etc will trigger the switch, but div
won't.) (Rules following the switch can start with whatever.)& div
or :is(div)
if you need to start a selector with a type selector.) (This employs the same parsing strat as (2.3) to avoid accidentally parsing invalid properties like //color: red;
as rules.)Arguments for each of the above options:
# | Pros | Cons |
---|---|---|
(1) |
|
|
(2.1) |
|
|
(2.2) |
|
|
(2.3) |
|
|
(3) |
|
|
Instead, that . would cause us to attempt to "parse an ambiguous rule", and when we hit the ; we'd realize it was instead an invalid property all along and just drop it, without changing the parsing mode.
Ah, I missed this detail, thanks. This does have the downside that a semicolon after a nested rule means that rule is thrown away. Could trip people up.
.foo {
.bar { color: red }; /* not applied. remove the semicolon and it works! */
}
Nope, that's fine too, because the }
will cause the algo to return the rule successfully (and trip the parsing switch). It won't continue looking forward after it's found a complete rule.
The ;
will then screw with the next rule, but that's true of any garbage following a rule. In your precise example it does nothing since there's no following rule.
Syntax is same as Sass/etc-style, which many authors are used to. (And is arguably just a good design.)
Can we be specific about the Sass comparisons?
This proposal would only make it more similar to Sass styles nesting in one very specific aspect .foo { .bar {} }
.
It will not allow BEM style selector concatenation and it will not match the same elements when complex selectors are involved and &
is not part of the first compound selector.
2.x : Can we list "requires a parser switch mechanic" as a Con?
2.3 and 3 :
Prevents us from ever changing property syntax to start with an ascii glyph. (Like
+transform:...;
for additive properties?) (But these are probably already ruled out anyway, due to people using garbage to "comment out" their properties, like//color: red;
, or*color:red;
for an old IE hack.)
It also limits future combinators and selector syntax because the ambiguity goes both ways.
Some examples which haven't come up yet. These might be fine, but they illustrate the point.
.foo {
color: green;
* {
color: purple;
}
}
.foo {
color: green;
||td {
color: purple;
}
}
It will not allow BEM style selector concatenation
Right, BEM-style concatenation was always a mistake and will never be part of CSS. However, anyone not using BEM or similar (or using a preprocessor with a similar syntax that doesn't have that feature) will be fine.
and it will not match the same elements when complex selectors are involved and & is not part of the first compound selector.
I don't understand what you mean by this. .foo { .bar & {...} }
will match the same elements in both contexts (equivalent to .bar .foo
).
It also limits future combinators and selector syntax because the ambiguity goes both ways.
I'm not sure I understand what you mean. We can continue to innovate in any way that doesn't make properties start with a non-ident (already probably necessary) or rules start with idents. All your examples, and all similar ones with new selectors or combinators, will be fine in the future.
I don't understand what you mean by this. .foo { .bar & {...} } will match the same elements in both contexts (equivalent to .bar .foo).
.foo .bar {
@nest .other & {
color:green;
}
}
css: .other :is(.foo .bar)
sass: .other .foo .bar
But these are probably already ruled out anyway, due to people using garbage to "comment out" their properties, like
//color: red;
, or*color:red;
for an old IE hackI'm not sure I understand what you mean. We can continue to innovate in any way that doesn't make properties start with a non-ident (already probably necessary) or rules start with idents. All your examples, and all similar ones with new selectors or combinators, will be fine in the future.
I am maybe misunderstanding the first quoted statement. So maybe it is fine if *
was used for some IE hack.
[example]
Ah, yeah, sure.
I am maybe misunderstanding the first quoted statement. So maybe it is fine if * was used for some IE hack.
Yes, it's fine if you write the parser change carefully, which I did. ^_^
I am not a huge fan of the proposed trick to change the parsing mode. I find it extremely confusing. In addition to that, it will be a pain for all libraries that need to parse CSS.
I have been thinking about this since I saw the proposal, but didn't have a concrete alternative to propose, but now I do.
How about we instead allow "post-nesting"?
article {
color: gray;
}
@nest {
h1 { color: black; font-weight: bold; }
h2 { color: black; }
}
or
article {
color: gray;
} & {
h1 { color: black; font-weight: bold; }
h2 { color: black; }
}
Pros:
&
everywhere for selectors that do not require itCons:
I am not a huge fan of the proposed trick to change the parsing mode. I find it extremely confusing.
I've been trying to wrap my head around some of the proposed draft nesting rules, finding them a little confusing as well. I never use SASS, so personally do not benefit from incorporating any SASS syntax.
On the other hand @FremyCompany's approach:
article { color: gray; } & { h1 { color: black; font-weight: bold; } h2 { color: black; } }
Is simple, clear, intuitive. Keeping the nesting & outside of brackets alleviates the ambiguous "is it a property or selector" parsing issue.
Only, how does it work for multiple nest levels? And split nests? Also, I'm concerned that closing a selector's bracket before nesting is counter to how other languages nest things such as if
or for
statements.
So, following this idea to use ampersand to define a nest group instead of in front of every nested selector, would it make more sense to use a different bracket type for enclosing nested selectors, either singly or as a group, which also allows not closing a selector before nesting as in the above example?
Here using &[] to define nest groups:
section {
margin: 0;
padding: 0.5em;
& [
article, aside {
color: #010a12;
background-color: #e6e0dd;
& [
h1 { color: #444; font-weight: bold; }
h2 { color: #333; }
.myClass { font-family: Barlow, sans-serif;}
]
}
aside {
background-color: #cde;
& [ p {font-size: 0.95em;} ]
}
]
}
So here, nests are fully enclosed in square brackets (though perhaps () or <> might be more appropriate?) with I believe the same advantages as @FremyCompany's idea, though making multiple nest levels and nesting hierarchy more clear.
So, relative to the above example:
article, aside {
& [ p {font-size: 0.95em;} ]
}
Is essentially the same as
article p, aside p {font-size: 0.95em;}
Instead of an ampersand, what if we used a $ as the symbol for the parent selector(s)? In this usage, $ or $0 would mean all of the listed parent selectors, and $1 to $9 would be each parent selector in the list, in order.
article, aside, span {
$ [ p {font-size: 0.95em;} ]
$1,$2 [ p { color: red;} ]
$3 [ p {color: green;} ]
}
So this would be functionally the same as:
article p, aside p, span p { font-size: 0.95em;}
article p, aside p { color: red;}
span p { color: green;}
}
The advantage here is more granular control over what parent selectors we are nesting under.
Thank you for reading
How about we instead allow "post-nesting"?
Non-nested nesting means we can't nest two levels deep in an unambiguous way.
So, following this idea to use ampersand to define a nest group
The group has already rejected "extra level of nesting" proposals - it polled very badly with authors.
Hi @tabatkins
Non-nested nesting means we can't nest two levels deep in an unambiguous way.
_The group has already rejected "extra level of nesting" proposals - it polled very badly with authors._
I'm not sure I understand: statement one suggests the desire to nest two levels deep, but statement two say no extra levels of nesting...
??
And well before I saw this, I opened a new issue #7877 which is a much more complete discussion and more thought-out method/syntax than what I posted here. It's not really about "extra level" it is about the benefit of grouping, and supports unambiguous levels, and specific selection from a list of parent selectors.
How about we instead allow "post-nesting"?
Non-nested nesting means we can't nest two levels deep in an unambiguous way.
I don't see why?
article {
color: gray;
}
@nest {
h1 { color: black; font-weight: bold; }
@nest {
b { font-weight: 900; }
}
h2 { color: black; }
}
===
article { color: gray; }
article h1 { color: black; font-weight: bold; }
article h1 b { font-weight: 900; }
article h2 { color: black; }
Non-nested nesting means we can't nest two levels deep in an unambiguous way.
@tabatkins Why? It seems to me that
article {
color: gray;
@nest;
h1 {
color: black;
font-weight: bold;
@nest;
span { color: pink }
b { color: orange }
}
h2 {
color: black;
@nest;
span { color: cyan }
b { color: yellow }
}
}
could be
article {
color: gray;
}
@nest {
h1 { color: black; font-weight: bold; }
@nest {
span { color: pink }
b { color: orange }
}
h2 { color: black; }
@nest {
span { color: cyan }
b { color: yellow }
}
}
It would be somehow like @else
I guess: linked to the previous rule. But would only be valid immediately after a style rule.
I'm not sure I understand: statement one suggests the desire to nest two levels deep, but statement two say no extra levels of nesting...
@Myndex I think statement 2 refers to indentation levels
I think the "post-nesting" examples (https://github.com/w3c/csswg-drafts/issues/7834#issuecomment-1277440033, https://github.com/w3c/csswg-drafts/issues/7834#issuecomment-1277440428) that use two levels of nesting are extremely confusing to read. Keeping nested selectors nested syntactically within the right pair of {}
makes things much clearer, and we should continue to focus on finding an acceptable syntax that does that.
I think the "post-nesting" examples (#7834 (comment), #7834 (comment)) that use two levels of nesting are extremely confusing to read. Keeping nested selectors nested syntactically within the right pair of
{}
makes things much clearer, and we should continue to focus on finding an acceptable syntax that does that.
Agreed. Can we just rule these out early (and those that introduce extra levels of nesting that have been ruled out in #4748) so we can keep the conversation more focused?
I don't find François' proposal confusing, and I like that it clearly separates properties from nested rules, and the consistency with @else
. Do you also find @else
confusing? If not, what's the difference?
I admit I also fail to see where the confusion stems from, but if a few people feel that way it might be good to try to understand the sentiment. Is that caused by the lack of whitespace? Usually, when I write CSS, I separate selectors by a while line, and I don't frequently make everything one-liner like in these examples.
To me, the mixing of declarations and selectors is significantly more confusing than using a different block. This is not clear to me at all at a glance, even with a single level of nesting; for instance:
a {
color: blue;
@nest;
&:hover {
color: red;
}
}
(this remains true if we make the @nest
optional, in my opinion, because it's not obvious at first sight that we have switched mode)
For what it's worth, you can essentially just take the "required @nest;
" proposal, replace the @nest;
s with } @nest {
, and you've got François' proposal. If you don't like how the indentation goes back a level before nesting (which I would understand and agree with) the you can simply pretend like @nest;
is a thing, but write it as } @nest {
.
This (François' proposal):
a {
color: blue;
}
@nest {
&:hover {
color: red;
}
}
versus this (François' proposal, re-formatted):
a {
color: blue;
} @nest {
&:hover {
color: red;
}
}
versus this (the @nest;
proposal):
a {
color: blue;
@nest;
&:hover {
color: red;
}
}
The benefit of } @nest {
over @nest;
is that there is no parser mode switch, and we can re-use the syntax construct we're using for @else
.
To me, the mixing of declarations and selectors is significantly more confusing than using a different block.
I disagree with this, but this is just a personal preference; I believe it's quite easy for humans to tell declarations apart from selectors, and even if it wasn't, we use syntax highlighters to help us with this type of thing. The good thing about the } @nest {
is that we can choose which we like more, and we can choose to dedent the @nest
block to match the selector for the block it's nesting for, or we can choose to indent it and level the } @nest {
with the declarations instead, like you would with @nest;
.
Can we just rule these out early (and those that introduce extra levels of nesting that have been ruled out in https://github.com/w3c/csswg-drafts/issues/4748)
That issue did indeed rule out an extra level of nesting, but François' proposal doesn't do that at all. It introduces a separate block for the nested styles without introducing another nesting level. It's actually quite clever. And, let's try to give suggestions a fair chance - this is an open discussion and I think everyone's proposals deserve to be looked at and considered. If it does indeed fail to meet some criteria we've previously mentioned, we can rule them out. But this one, I don't think does that.
Every other context in CSS where something is nested in another thing, the rule is actually nested syntactically. Doing a sibling instead would be a huge departure in syntax, mental model, and CSSOM. Without a major advantage to this "sibling" approach (and major disadvantages for every other approach that actually nests), I'm happy to reject this approach out-of-hand.
@tabatkins It's not a departure since we already have @else
. I fail to understand how these arguments apply to "post" @nest
but not to @else
. I'm just puzzled because I don't see the problems, but it seems most people do, so I'm worried @else
may need to change too?
@Loirooriol The difference though is that you wouldn’t nest an else
within the if
– it is a sibling to the if
, so it makes sense to keep it like that (also see: any other programming language).
For nesting rules in rules, the logical thing to do, is to actually nest it syntactically. I mean, by making them siblings, CSS (as a language) would make a fool out of itself tbh – I can already see the 🤡-memes incoming.
And as Tab pointed out, we already use nesting a lot: nested at-rules, rules nested in at-rules (media queries, layers, scope, …), etc.
Yeah I find @FremyCompany's solution both odd for not being nested, and also somewhat elegant in reusing existing syntax. The departure is that @else
is not related to one thing being inside the other, but is explicitly the opposite. Conceptually, selectors in @media
are applied when 'inside' a matching media, and so on. Else appends a 'but otherwise' clause, which makes sense as conceptually un-nested. I go back and forth on it - don't hate it, but also likely wouldn't vote for it.
I've been thinking of this from a 'how would I teach this' perspective - and I think it would be easy to teach. But mostly I'm drawn to @LeaVerou's approach, which I would teach as: always start nested selectors with a symbol. The problem cases (elements with descendant combinator) aren't really an edge case, but they're generally straight-forward to fix using this rule. @scope
and >>
may also reduce the number of those cases.
(Even though -
is technically a symbol, and part of declarations, it's not a symbol we allow at the start of selectors - so the rule holds for nesting.)
Yup, what Bramus said - if/else isn't expressing a parent/child relationship (it's alternatives at the same level), nor is it written in a nested fashion in virtually any programming language. (Certainly zero mainstream langs, but there are so many langs it's hard to say definitely nobody does this.) We very specifically rejected expressing @else
with a nested grammar for that exact reason.
I was confused when Lea described @FremyCompany's proposal, but seeing it, it kinda makes sense. Basically you have, potentially, two blocks associated with the selector: a block of declarations and a block of nested rules. That they're siblings isn't entirely unreasonable.
I still think @nest is noisy, and would prefer not having it. You could just drop @nest and have bare braces, though. Nesting is going to be so fundamental to CSS, it really needs to read cleanly.
article {
color: gray;
} {
h1 {
color: black;
font-weight: bold;
} {
span { color: pink }
b { color: orange }
}
h2 {
color: black;
} {
span { color: cyan }
b { color: yellow }
article & {
font-size: 110%;
}
}
}
@LeaVerou mentioned that @FremyCompany's proposal would mean the CSSOM and syntax would diverge, since the rules would be nested inside the style rule representation but be in a separate rule... but I think it makes sense to think of it not as a separate rule, but as giving a style rule an optional second block.
Conceptually, style rules would now have three parts:
Using bare braces or something like &&
makes this association between the selector and the second block clearer than using @nest
; I think it's reasonably consistent with the desired CSSOM structure.
Conceptually, style rules would now have three parts:
- a selector
- a declaration block
- a style rule block (optional)
This is such a cleaner model, I love it :)
I am totally on-board with the idea of dropping @nest
entirely, and just have the second block follow the first one.
@FremyCompany It's a pretty clean mental model, but the downside for using bare braces is that the first declaration block is required, and having an empty pair of braces looks pretty awkward when you have nested rules but no declarations for the parent selector... Using something like && { rules }
instead of { rules }
solves that problem (you can prepend a selector with no ambiguity) but then people don't like excessive ascii...
Using something like
&& { rules }
instead of{ rules }
solves that problem (you can prepend a selector with no ambiguity) but then people don't like excessive ascii...
That would be extremely confusing for selector lists. E.g. here:
a, b && {
.foo {
/* ... */
}
}
The "generated" selector is conceptually a .foo, b .foo
(actually it is :is(a, b) .foo
), but it looks like it would be a, b .foo
.
Folks, @fantasai and I worked on summarizing the current state of proposals (starting from @tabatkins' excellent summary here), you can find it here:
We went with a MD file instead of an issue comment so that everyone can edit/send PRs.
In preparation for tomorrow's discussion, we think it would be useful to see where we stand in terms of consensus, so we added a table of participant preferences, and we encourage everyone to add their positions to the table as well (by edit or by PR)!
Apologies, but a non-nested nesting syntax is significantly a non-starter for me. It's a completely novel syntax form not attested by any other nested thing in CSS (native or preprocessor tools), and it comes with some significant learnability concerns (as commented above). As one of the spec editors, I don't find it an acceptable outcome.
I am not happy with calling "non-nested" the nested-element
selector in
element { property: value; } {
nested-element { ... }
}
It is nested in a pair of brace, just like it always has been in all proposals. It is just not the same pair of brace as the declarations. But, then again, CSS so far has always had blocks that contained only either declarations or rules.
I am not sure how "weird" this really is. This is exactly analogous to HTML.
<element property="value">
<nested-element ... />
</element>
both the properties and the child elements are nested in element
, but in two different context that are both associated to the same thing. When you don't need the nested elements, you can use <element />
which is what CSS has had so far.
I really don't see the weirdness in any of this, this is perfectly analogous with CSS.
PS: I am 100% fine with the group going with another solution than 4; honestly there are pros and cons to all. But I am not fine with having discussions where there are people "vetoing" options on the basis of personal preferences and justifying this using authority arguments like "as one of the spec editors". This is unprofessional. Please keep an open mind? 😕
It is nested in a pair of brace, just like it always has been in all proposals. It is just not the same pair of brace as the declarations.
Yes, that's the non-nested part I'm talking about. It's a sibling to the rule that it's trying to nest within.
PS: I am 100% fine with the group going with another solution than 4; honestly there are pros and cons to all. But I am not fine with having discussions where there are people stating that "because I am the editor, I don't want to hear about other people's opinion if they don't agree with me". This is unprofessional. Please keep an open mind? confused
This is not remotely what I said. Do not attribute offensive statements to me, particularly in a quoted fashion. Please rephrase or delete that comment.
@tabatkins Sorry, you are totally right; I used quotes to express how I felt after reading your comment rather than your comment itself; this is not aceptable and I present my excuses for this.
Are the @nest
blocks part of proposals 2 and 3?
I know enforcing verbose syntax isn't popular but some really like the low complexity of @nest
(it just works).
.foo {
color: green;
@nest & anything {
color: purple;
}
}
Are the
@nest
blocks part of proposals 2 and 3?
I don't think they are at this point. I do think that could theoretically be added to 3 as an option (maybe also 2, but that's a bit weirder).
Conceptually, style rules would now have three parts:
a selector a declaration block a style rule block (optional)
I think a big pro with this approach proposed by @fantasai & @FremyCompany is that it minimizes the extra restrictions and further "parser no-go"s we'll have to impose on future syntax, and we know how much users hate that and find that confusing. I think, instead, it's rather another opportunity to maintain current syntax flexibility, with just a tiny compromise on nesting-syntax paradigm. Yes, it's a slight paradigm shift, sort of Functional vs. OO, and I think that is how we should try to approach it.
I also think that with a bit more of bikeshedding we can also overcome the .a, .b & {...}
issue (I can already think of a few noisy ones 😋 )
Are the @nest blocks part of proposals 2 and 3?
Yes to the (2) proposals. (It's needed to trigger the switch when nothing else is around to do so.) No to the (3) proposal. (There's no switch at all, every rule is unambiguous on its own.)
Yes to the (2) proposals. (It's needed to trigger the switch when nothing else is around to do so.)
So both of these would valid in (2) :
(question was specifically about at-rule blocks, not the block-less rule @nest;
)
.foo {
color: green;
@nest & anything {
color: purple;
}
}
.foo {
color: green;
@nest;
anything {
color: purple;
}
}
But @nest
would not exist at all in (3)
Edit: 👉🏼 👈🏼 (from https://github.com/w3c/csswg-drafts/issues/7834#issuecomment-1282776786 )
As mentioned in #7796, there are some problems with the currently-proposed nesting syntax:
@scope
, which invites a lot of copy-paste errors when transferring code among these contexts&
within all the selectors in a list even beyond syntactic disambiguation (instead of allowing relative selectors) is annoying to type, easy to forget, and makes automated (and manual) conversion of existing code much more difficult (requires selector parsing rather than regex).&.foo
and& .foo
are easily confusable (both for reading and for typing), and so long as the latter is required for nested relative selectors this will be a commonly encountered problem