Closed plinss closed 1 year ago
As an example, if this were implemented first, we could never have added custom properties with the current syntax (which redefined identifiers).
This isn't accurate. We couldn't have naively redefined identifiers; we'd have had to do some compat analysis to check if it would actually cause a problem or not. (And we'd have found it was perfectly okay, since --foo
wouldn't be the start of any valid selector anyway, so usage would be minimal.)
The hard restriction we have is that we can't introduce new property syntax that looks like a (relative) selector. So nothing that puts any of >+~.:#[
at the start, for example. (.:#
could be used if followed by whitespace or some other non-ident, tho.)
(Unless we end up solving the perf problems with the unbounded lookahead. Then we can potentially do a lot more so long as they're eventually distinguishable, tho we'd likely still want to be careful about mixing things too hard.)
Alternatively, we could limit selector combinators to the current set
This won't help, as descendant combinators mean the nested selector can start with any of the simple selector glyphs (.:#[
). We'd also have to restrict our ability to add new simple selectors, not just combinators.
"Mandatory @nest
" was rejected by the group already, and verbosity wasn't the sole concern about it; copy/paste ability is still affected even if you reduce the required prefix to a single character.
- We remove the lookahead restrictions on the parser and 'simply' adopt the SASS syntax… I'd like to see some experimentation and real-world data to check that assumption and see if advancements in processor speed and RAM availability allow us to relax that.
I believe everyone agrees that removing the lookahead restrictions on the parser and adopting SASS-style syntax would be ideal. This is not something for which we can debate the pros and cons. It's all pros.
Implementation experience is the only path forward to find out whether or not if this is a viable alternative. The teams currently implementing CSS Nesting need to try it, test performance, and see what happens. To attempt to figure out a way to make it fast. If so — fantastic, decision made. If not, well, decision made.
I know folks are already working to see such implementation experience happen. Meanwhile, we should discuss what to do if removing the lookahead restrictions is not a viable solution.
@tabatkins and I both disagree that splitting style rule and declaration parsing on non-ident start over-restricts the future of CSS declaration syntax. But to understand why, we need to consider:
We're not just reserving everything that has an ident start for declarations: we're reserving everything that results in an invalid sequence, whether that sequence is parsed in as invalid style declarations or invalid style rules or a mix of both.
Remember the parsing rules being proposed for mixed context are a little different:
The following sequences are all complete invalid constructs: they are thrown away and anything after them starts a new parse. Which means we can interpret them however we want in the future.
@foo anything except semicolon { anything }
random stuff: other stuff { anything } anything;
random(stuff): other stuff;
!other stuff: more stuff;
++extra stuff: stuff;
(anything): anything;
The only thing we're closing off entirely is a new construct that maps to the following sequence:
Basically, we can't introduce new things like:
#stuff starting with a symbol { anything in this block } valid-property: value;
/* new thing all parsed as one declaration */
which doesn't seem very likely for us to add in the first place.
So as long as we're not planning to introduce a new type of property declaration that maps to that, we can introduce any new declaration syntax additions we want in the future. Though for the sake of preserving LALR parsing, we'll want any symbol start to be something that is not currently valid as a selector start token.
I think this is plenty of open space for innovation.
"Mandatory @nest" was rejected by the group already, and verbosity wasn't the sole concern about it; copy/paste ability is still affected even if you reduce the required prefix to a single character.
Yet a significant number of poll respondents advocated for a mandatory &
which has the same copy/paste problem, yet seems to be acceptable to developers.
One of my opinions on this whole matter is that what people really want is SASS's behavior, if we can't give them that, then giving them something that's kinda close but has all sorts of weird rules and exception cases (and requires ugly hacks like wrapping perfectly normal selectors in :is()
for syntactical purposes) isn't all that helpful in the end.
If people don't like what we deliver and prefer SASS, they can just use SASS. What we deliver should be consistent, logical, easy to explain, and not constrain the future development of the language in any way just to try to mimic a syntax that we really can't mimic properly.
giving them something that's kinda close but has all sorts of weird rules and exception cases
Hyperbole isn't useful here. There is precisely one rule - if your selector starts with an ident, you have to rephrase it somehow.
Not trying to be hyperbolic, and it's not one rule if the answer is 'somehow'. First, authors have to understand exactly what an ident is, then sometimes you have to use a leading &
, and other times you have to wrap at least the leading selector in :is()
(and yes, that's a hack). Sorry, but that's weird, and while the reasons make sense to us, they won't make sense to most authors. Then, in the future as we add more selector syntax or property syntax the set of rules and exception cases can grow. That's problematic.
"what's an ident" is extremely simple - if it starts with a letter or a dash (and isn't a number). "it feels like a keyword" works just fine for author intuition.
For fixing it, you can do :is()
all the time. Appending a &
compound is just shorter sometimes. That said, I've just posted a suggestion in #8253 that allows for a completely uniform super-short fix.
My point about idents isn't that it's hard, it's that it's one more thing. i.e. the syntax exceptions aren't 'just one simple rule'. (Also, you forgot that idents can start with escapes, underscores, and non-ascii... In Jenn's poll, she even got the description of when an &
is necessary wrong, as she forgot custom properties. So don't try to tell me this ins't error-prone.)
The rules at this point are:
1) if your selector doesn't start with an ident, use as-is (and know what an ident is), or optionally prefix with &
2) if it starts with an ident, you must prefix with &
3) unless you need to use &
somewhere else inside your selector, the wrap the first selector in :is()
(And, really, is adding :is()
all the time an improvement? I know you're not seriously expecting people to just always wrap the first selector in :is()
, but I can see tools doing it.)
Prepending every nested rule with @
actually is one simple rule and there's no special casing, ever. Every selector syntax available now or in the future 'just works' (we already excluded @
from selectors) and there are no conflicts with future extensions to property syntax.
@plinss
We remove the lookahead restrictions on the parser and 'simply' adopt the SASS syntax. The lookahead restriction came about 25 years ago when there were real concerns that CSS would be too slow to ever implement in a browser and everything was focused on performance. I'd like to see some experimentation and real-world data to check that assumption and see if advancements in processor speed and RAM availability allow us to relax that.
I agree and I have opened an issue about this here with an algorithm that would minimize the performance hit: #7961 Here is a summary:
Sadly the issue was later derailed a bit by those who think &
should be mandatory.
One real issue that came out of it was (raised by @lilles) that we'd have to make {}
invalid in custom properties for that to work, and there is some limited usage of that in the wild (<0.01%), so it's a tradeoff. (IMO still a worthy one).
@jensimmons
Implementation experience is the only path forward to find out whether or not if this is a viable alternative. The teams currently implementing CSS Nesting need to try it, test performance, and see what happens. To attempt to figure out a way to make it fast. If so — fantastic, decision made. If not, well, decision made. I know folks are already working to see such implementation experience happen.
See above, when I tried to discuss it in #7961, the Google implementor was very negative:
I don't share this goal; I would like us to just get the syntax (any syntax) specified, out the door and be done with it. So no, I won't be spending resources trying to bend our minds around how we tokenize CSS to this avail; I can NAK what is a non-starter for us, and that's what you'll get.
Great to hear things have changed since then and folks are working on it. Is it the same folks? Which folks are working on it?
In my mind, a merged version of options 3 and 1 tick all the boxes for trying to detect nesting:
>+~
? You’re nesting..:#[*
? You’re nesting.&
combinator? You’re nesting.@nest
(to be used if you want to use &
somewhere but not at the start)? You’re nesting.Anything else would fall back to the current mechanism to detect declarations, nested at-rules, etc.
Rules 3 and 4 in the list of checks above are the bail out mechanism for when an element selector is nested, similar to what option 1 of the syntax provided us with. These two rules (3 and 4) also play nice with the first two rules, as they can be used together: for example teams can choose to make @nest
mandatory by using a linter, while giving other people the freedom to not do so.
(Note that I’m sticking with @nest
instead of @
for reasons explained here)
As an author myself, who has written a lot of CSS both with and without SASS, I don't find these restrictions and workarounds confusing at all. Once people do it a few times they will quickly get used to it. It isn't like even SASS didn't also have things to keep in mind: like always have a semicolon after the last declaration before the nested selectors start. That one still trips me up sometimes, but I know to look for it when there is a problem. And if I have to choose between ALWAYS preceding each rule with @
or @nest
, or just doing it when the selector starts with a letter (or underscore, ideograph, or dash or escape), then I'd much much prefer the latter.
Sure something like :is(div)
is ugly and sort of a hack, which is why I would like to be able to choose a bare @
as an non-mandatory prefix instead, but even without that I'd like to move forward with what we have. I don't think it is at all burdensome to the author.
The CSS Working Group just discussed CSS Nesting Syntax
, and agreed to the following:
RESOLVED: Adopt Option 3
I'd like to point back to @fantasai's comment about what precisely the syntax constraints we're actually adopting are.
There are two aspects: things that are currently valid, and things that are currently invalid.
For things that are currently valid, we are indeed closing the door on options. With the current spec, we cannot allow a declaration to start with +
, for example, as that's a valid relative selector. (At least, not without paying a lookahead tax that we're currently rejecting.) We cannot start a selector with an ident. As I've said in previous threads, tho, starting a declaration with a symbol is already very likely off the table, given the prevalence of people using a random ascii symbol to "comment out" a property. So in practice we're closing the door on very little new space that wasn't already verboten.
For things are the currently invalid, we are in general not closing the door on anything. CSS's existing forward compatibility parsing rules give us a lot of leeway to reinterpret things, ensuring that "new stuff" (aka invalid stuff in an older browser) gets properly isolated and ignored without screwing up surrounding stuff that is recognized in an older browser.
The recovery rules are:
This suggests that (a) we want to bias toward parsing unknown things as nested rules, since the recovery is stricter/safer, and (b) if an older browser parses some pattern as an (invalid) nested rule, and we decide to make that pattern a declaration instead, this is fine so long as it doesn't contain a {}-block followed by something that looks like a complete (prop:val;
) declaration.
That's the crux. The pattern we are locking ourselves out of is just %foo: {...} bar: baz;
being parsed as a declaration. That is such a ridiculously arcane pattern that I feel completely comfortable stating we'd never explore that space anyway, even if it did continue to be an option.
Notably, say in the future we decide that 1: red;
should be a valid declaration. Currently that'll trigger nested-rule parsing, but the semicolon will cause us to stop parsing and throw it out as an invalid construct, then resume parsing fresh afterwards. If we changed the rules so that browsers parsed that as a declaration, then older browsers will... see it as invalid, and throw away tokens until the semicolon, then restart parsing fresh. In other words, we have precisely identical error-recovery regardless of how it's categorized in older browsers.
starting a declaration with a symbol is already very likely off the table, given the prevalence of people using a random ascii symbol to "comment out" a property.
I'm not convinced by this, because 1) I don't see this statement backed by any data, and 2) a declaration preceded by a random symbol may stay as invalid even if we add a new feature that uses that same symbol.
Like, even if there is plenty of +width: 10px
on the wild, we might be able to add a new future that does something special for e.g. +width+: 10px
. Option 3 without lookahead closes that door.
A problem I have with the "it's only an error recovery problem" approach, is that once something new gets added to selectors or properties, it's closed off forever in use for the other case. For example, if we add %
as a special prefix for properties, we can never use it as a combinator (or vice versa). And that's not even considering cases we're already closing off, e.g. IIRC there's a proposal for extending list properties like: +backrgound: green;
, kiss that goodbye before it's even properly considered.
I still feel that's too constraining. Paraphrasing as I believe @Loirooriol said elsewhere, we are bending and constraining the language here in ways that are restrictive, and introducing new (and seemingly arbitrary to the non-initiated) rules that authors have to learn and follow, all for the benefit of avoiding a single character. (And if we chose a mandatory &
prefix, it just choosing a different character.) I just don't see the benefit being worth the cost.
And frankly, I don't think the copy/paste argument has all that much merit. As the proposal stands, authors cannot 'simply' copy paste rules into a nested context because they will have to manually scan them for the cases that need fixups, this will be error-prone. Big picture, I don't think we're really helping authors here. If authors have to add a simple prefix, that can be easily automated by tooling.
Other solutions that satisfy my concerns that I'm not sure have been properly considered are creating a nested rule context within an existing style rule, e.g.:
foo {
color: red;
@nest {
bar { color: blue; }
baz { color: green; }
}
}
Which could be simplified to:
foo {
color: red;
@ {
bar { color: blue; }
baz { color: green; }
}
}
or even:
foo {
color: red;
{
bar { color: blue; }
baz { color: green; }
}
}
A solution like the above does simplify copy/paste and avoids all the syntax rules and extension issues. (It also doesn't preclude allowing @ bar { ... }
for individual nested rules.)
starting a declaration with a symbol is already very likely off the table, given the prevalence of people using a random ascii symbol to "comment out" a property.
I'm not convinced by this, because 1) I don't see this statement backed by any data
I worked as a lead front-end dev in China for three months, in two cities with two development different teams. And it was mind-blowing for me what they were doing in their CSS. It was so different than what I was used to, even though I'd already worked with many dozens of different teams, on hundreds of projects, in multiple countries across Europe and North America, over two decades. And yes, I saw code that had random ASCII symbols all over the place to comment things out, or make them work one way for one version of IE while another way for another version of IE. The experience taught me there's an incredibly wide variety of how people write their CSS. Especially given how many web developers do not speak English — there's a lot of diversity and variety in what's considered "best practice" in one community / country / continent vs another.
@plinss That other solution was discussed in #4748, even initially approved in https://github.com/w3c/csswg-drafts/issues/4748#issuecomment-960145492, but reverted in https://github.com/w3c/csswg-drafts/issues/4748#issuecomment-1233176065. IIRC the concern was that it uses 2 levels of indentation, which can be inconvenient when having multiple levels of nested rules.
I guess I would prefer it over option 3 (which yeah, I feel like it's bending the language too much for little gain), but postponing the brackets instead of nesting them (i.e. option 4) avoided the double indentation so it was probably better.
@jensimmons I don't doubt that there are some people doing this kind of things, but I lack numbers to know if these are only some anecdotical cases, or if the breakage of adding a feature mixing symbols with declarations would be huge. Also, there might have been people using --
to comment out a declaration, but this didn't prevent custom properties since the effect is not observable unless you use var()
or JS APIs. So I'm not convinced that adding such new features is "very likely off the table" as Tab said.
I accept that using a nested block looks weird when the outer most rule is only a nesting container due to {{ ... }}
, but for that case we can always also accept the top-level @nest
syntax. I'm not convinced the extra level of indentation should be a deal-breaker (it certainly isn't worth adding the language constraints just to avoid IMO).
I understand that alternative approaches were discussed and rejected in the past, but it's not clear that all of those decisions were made with the same information as we now have. I strongly suspect some of those would have gone differently were we making them now. If we're going to get past this impasse we all need to be able to take a step back and look at the big picture.
I fear that many of the advocates of the current approach have gotten there by a long series of compromises trying to narrow down the options, which each may have made sense in isolation, but when taken in as a whole have led us to a solution where the costs outweigh the benefits.
I lack numbers
A Google search for "CSS star hack" returns 9.8 million results.
I lack numbers
A Google search for "CSS star hack" returns 9.8 million results.
It only returns 1660 results if you quote the phrase. And the majority of those talk about specifically targeting IE7 and below and are 9+ years old
With all due respect, @
is too much hack.
And yes, I saw code that had random ASCII symbols all over the place to comment things out, or make them work one way for one version of IE while another way for another version of IE.
@jensimmons As a Chinese developer, I'm curious about what kind of CSS, do you have an example?
I fear that many of the advocates of the current approach have gotten there by a long series of compromises trying to narrow down the options, which each may have made sense in isolation, but when taken in as a whole have led us to a solution where the costs outweigh the benefits.
This is not the case, and speculation of this sort isn't helpful. I know exactly what the costs are in aggregate (I laid them out in my last comment), and believe they are more than acceptable vs the costs of using a syntax that diverges from the common usage of nesting across more than a decade of preprocessors (and possibly introduces additional levels of indentation to every usage of nesting).
I know you disagree, @plinss, but if my previous post hasn't convinced you I'm not sure anything else will. I personally am not going to be convinced to go back to a mandatory prefix or additional nesting, either, and there's no middle ground between our two positions to make a compromise possible, so I suggest you go ahead and file your objection if you're going to.
I'm curious about what kind of CSS, do you have an example?
For example, check out this list of Internet Explorer parsing hacks.
And in my personal experience I've absolutely used //color
, *color
, or #color
to "comment" a property out.
Like, even if there is plenty of
+width: 10px
on the wild, we might be able to add a new future that does something special for e.g.+width+: 10px
. Option 3 without lookahead closes that door.
Sure, but it leaves open, say, width+: 10px
. This is my point - there's still so much space open for future development. I simply do not believe there is a Future Tab who will be more than momentarily annoyed with this syntax restriction before moving to an equally-good syntax that works.
A problem I have with the "it's only an error recovery problem" approach, is that once something new gets added to selectors or properties, it's closed off forever in use for the other case.
Note that I explicitly addressed this case in my earlier comment, even before I started talking about the error-recovery case:
For things that are currently valid, we are indeed closing the door on options. With the current spec, we cannot allow a declaration to start with +, for example, as that's a valid relative selector. (At least, not without paying a lookahead tax that we're currently rejecting.) We cannot start a selector with an ident. As I've said in previous threads, tho, starting a declaration with a symbol is already very likely off the table, given the prevalence of people using a random ascii symbol to "comment out" a property. So in practice we're closing the door on very little new space that wasn't already verboten.
The problem is that the current proposal does not match the "common usage of nesting across more than a decade of preprocessors". It already diverges by requiring prefixes in a significant percentage of the cases.
As an example, I have a number of projects using the StencilJS framework. That framework requires scoping every custom element style rule with an element selector. The stylesheets is these projects have all sorts of repetition and would benefit greatly from nesting. Over 90% of those style rules would require prefixing in the current proposal.
I also have to point out, again, that the rules for when prefixes are necessary aren't as simple as you represent. To this date I have never seen a public-facing description of the nesting syntax that accurately describes what an ident is.
I've heard too many times to count, and even from Google implementers, that it's easier to simply prefix every nested rule.
and there's no middle ground between our two positions to make a compromise possible
And here's the elephant in the room. There is a middle ground that satisfies my objection and actually does allow the common usage of nesting. We relax the lookahead requirement. This gets rid of all required prefixes and doesn't impose any restrictions on the evolution of CSS, in fact it opens up more avenues.
The problem is that Goggle's position here so far has been "non-starter, we're not even going to consider this", with no data or explanation why. Even though other implementers have said it seems doable and are willing to experiment. I don't believe Google's position here is an example of engaging in the standards process constructively.
If there were hard data showing why adding lookahead can't be done, then we'd be in a position where we have to compromise on something else, but there isn't, and we don't.
The problem is that the current proposal does not match the "common usage of nesting across more than a decade of preprocessors". It already diverges by requiring prefixes in a significant percentage of the cases.
And doesn't require prefixes in the majority of cases.
The stylesheets is these projects have all sorts of repetition and would benefit greatly from nesting. Over 90% of those style rules would require prefixing in the current proposal.
So the current proposal would save you some typing in 10% of the cases versus prefixing all the time, and for most people would save them a lot more.
To this date I have never seen a public-facing description of the nesting syntax that accurately describes what an ident is.
This is because the full, accurate description of what an ident is isn't relevant to the discussion. Nobody, and I mean nobody, writes a selector like \64 iv.foo
, so the fact that it's an ident (despite looking like it "starts with a symbol") doesn't matter. The ambiguous case in question is someone writing a property name (always ASCII letters, or a dash or underscore) vs a tagname selector (always starting with an ASCII letter, and usually composed of only ASCII letters).
I've heard too many times to count, and even from Google implementers, that it's easier to simply prefix every nested rule.
And we've also heard at least as many times (more times, if going by poll results) that people prefer not prefixing every single rule. The popular sentiment seems to be clearly tilted (slightly, but significantly) towards not prefixing everything.
And here's the elephant in the room. There is a middle ground that satisfies my objection and actually does allow the common usage of nesting. We relax the lookahead requirement. This gets rid of all required prefixes and doesn't impose any restrictions on the evolution of CSS, in fact it opens up more avenues.
The current spec is compatible with relaxing the lookahead requirement in the future. All it will do is make some currently-invalid CSS become valid; everything that's already valid under the current spec will remain so. The only thing that'll be encouraged to change is that selectors that would have started with an ident (and thus have to be spelled differently today) can be rewritten to be simpler, but every other selector remains exactly the same. And if those selectors aren't rewritten, well, they're still being spelled with existing valid CSS, not a legacy syntax that existed solely to fix Nesting parsing.
For every other option, relaxing the lookahead requirement in the future will mean there are now multiple ways to express any nested styles, and the previously-valid way (which presumably would remain valid) would become a legacy option that nobody uses, since it's longer. It doesn't seem great to leave behind an "oh yeah, you can put an @
before your selector in nested rules, it doesn't do anything but it's valid for some reason" legacy! (Or worse, "oh yeah, you can put an @nest in front of your parent rule, but then you have to wrap its properties in a nested rule" or "oh yeah, you can put your nested rules after the parent rule, in another {}-block, if you want", etc.)
The problem is that Google's position here so far has been "non-starter, we're not even going to consider this", with no data or explanation why. Even though other implementers have said it seems doable and are willing to experiment. I don't believe Google's position here is an example of engaging in the standards process constructively.
Steiner, personally, isn't interested in doing the perf explorations to figure out if infinite lookahead is viable. Other people are, both inside and outside of Google, and I feel confident that we'll get those explorations and see what the results are. If it turns out the way we all want, great, the current spec has the shortest path to accommodating that change, with the least amount of legacy cruft left by the wayside. If not, the current spec is the closest we can get to that syntax, and that has value to authors (as proven by their repeated votes and comments).
If there were hard data showing why adding lookahead can't be done, then we'd be in a position where we have to compromise on something else, but there isn't, and we don't.
Absent any newer experimentation, what we do have is the statements from several years ago when Nesting was first broached, where impls were pretty sure the perf hit to parsing would be too much. These positions can absolutely change over time, but we can't assume they will. I'm not willing to stake the spec's survival on something that, at last check, was a multi-implementation non-starter.
And doesn't require prefixes in the majority of cases.
[citation needed] Do you have any data to back this claim up? And it is a 90/10 majority or more like a 51/49 majority?
We do have data that shows slightly more that half the authors will simply add prefixes anyway. And for the other half allowing them to omit prefixes 50% of the time is only serving 25% of the use cases.
IMO a solution that only delivers to 25% of the use cases and has real costs to both learnability and the evolution of the language as a whole is a bad tradeoff. And even if your majority of cases is 99%, it's still not a solution for 50% of the authors.
Also, my personal prediction (based on experience) is that authors who say now that they will omit prefixes where they can will grow frustrated with the errors they encounter from forgetting them where they are needed and will shift over time into the "always prefix" camp.
The current spec is compatible with relaxing the lookahead requirement in the future
Agreed, however if it turns out we can't relax lookahead (at least for a significant amount of time), we're stuck with a solution that adds constraints to the evolution to the language.
The other solutions are also compatible with relaxed lookahead. Having multiple was to express nesting isn't harmful and some authors may simply prefer the more explicit alternatives due to clarity. Carrying the legacy syntax for a time isn't that costly.
Other people are, both inside and outside of Google, and I feel confident that we'll get those explorations and see what the results are.
Good to hear (and this is the first time I'm hearing it). So given the fact that there's a real cost to pay to for one of the solutions, doesn't it make sense to get this data before making a binding decision and shipping code? Particularly if that data may allow us to resolve all the issues and deliver a solution that makes everyone happy and covers 100% of the use cases?
I also need to point out that the push for the syntax decision isn't being phrased as "We're really going to try to relax lookahead and just deliver the SASS syntax, but that's going to take time, and we want to ship an interim solution in the meanwhile". If that were the case, it changes the dynamics of the equation. Because frankly, I've been waiting 25 years for nesting, and if it means I have to wait another 6 months for a real chance to get a good implementation, I'd rather wait. If we know for a fact that the better way of doing can't be done at all, or maybe not for another decade or so, then the push for delivering something makes more sense.
[citation needed] Do you have any data to back this claim up? And it is a 90/10 majority or more like a 51/49 majority?
Most selectors in the wild are based on classes, pseudo-classes, or attributes, just as a rule. Element selectors are a minority in complex selectors. And to be problematic for our purposes, the element selector has to be part of the first compound selector in the nested rule's selector, and the nested selector has to be either non-relative, or relative with an implicit descendant combinator.
For confirmation you can just go look at the CSS from a random collection of popular websites.
So given the fact that there's a real cost to pay to for one of the solutions, doesn't it make sense to get this data before making a binding decision and shipping code? Particularly if that data may allow us to resolve all the issues and deliver a solution that makes everyone happy and covers 100% of the use cases?
There is a cost to pay for all syntax options. In a significant way, the major decision is whether we're pushing the cost to all authors (requiring a more verbose syntax) or to future spec authors (requiring more careful syntax design in the future). As I've argued, I think the latter cost is actually extraordinarily small in practice.
If we know for a fact that the better way of doing can't be done at all, or maybe not for another decade or so, then the push for delivering something makes more sense.
Again, the current spec is pretty trivially switchable into an unlimited-lookahead syntax. All other approaches require us to be left with a legacy syntax in addition to the unlimited-lookahead syntax. So if we want to ship something ahead of confirmation (or denial) that unlimited-lookahead is viable, the current spec is still the best way to do so.
@tabatkins You mentioned that other folks at Google are looking into this. Is this exploration happening somewhere in the open? If so, could you point us to any of the relevant issues? I’m very interested in the specifics, and I suspect others may be as well (and who knows, might even be able to provide input — lots of smart folks here). Thanks!
Nobody has started on it yet, there's just been discussion.
One question I would like to add before today's meeting: can we at least make the current draft a lookahead of 1
?
The proposal would be as follow: to be considered a property declaration, the current token sequence should start with an identifier immediately followed by a colon (notwithstanding whitespace).
This would allow to drop the &
in many more cases where selectors start with a tag name (which unlike what some seem to argue, is very common). It would however break for selectors like a:hover
but I think this is reasonably easy to explain to developers, and also will be visibly different in syntax highlighting, making it directly clear what the error is (even without explanation).
This will reduce the burden while converting stylesheets to nested rules quite a lot, and would reduce the "syntactic disadvantage" given to tag selectors in CSS.
If, at a later date, implementors want to pursue further lookahead relaxation, this is still possible, but at least, we already start by enforcing some.
A Google search for "CSS star hack" returns 9.8 million results.
@jensimmons That may be the case for *
, but the claim did cover several other symbols like +
. I'm not aware of +
being frequently prepended to declarations, and I'm still not seeing any data showing that.
It would however break for selectors like
a:hover
@FremyCompany I'm afraid this would just cause further confusion, and authors would rightly complain about the CSS design making no sense.
This will reduce the burden while converting stylesheets to nested rules quite a lot
Why should this be a goal at all? Existing stylesheets can stay as-is.
The CSS Working Group just discussed [css-nesting] Problem with mixing properties and selectors
.
One of the claims I have heard is that we can ship option 3 now, then in a lookahead future we can update nesting syntax. Could someone elaborate on what that update would look like?
I am also interested in exploring the suggestion from @bramus about shipping a subset of option 3 with a required & to start, then gradually adding more functionality over time. That seems like a promising compromise, though it would likely make the @supports
story much more complicated
To be clear, my objection comes from shipping option 3 without knowing if we can relax the lookahead restriction or not. Because if we can't, it carries the syntax restrictions, is an author foot-gun, and only satisfies significantly less than 50% of the use cases (vs a mandatory prefix). I don't feel the benefits outweigh the costs. If we covered 100% of the use cases then the math might work and it'd be worth it, but we're not there.
I'm fine with shipping a solution (like @bramus's suggestion) where a prefix is required for now, and we can make the prefix optional in the future if lookahead proves viable, or we can decide to make it optional in some cases later if lookahead proves to be impossible (essentially morphing it into option 3 or some flavor with limited lookahead and more options to omit the prefix).
I'm also fine with shipping option 3 as-is, as soon as we know for a fact that look-ahead is viable, as an interim step until full lookahead can be implemented, because all the down-sides are temporary. (I personally don't think it would be worth shipping, but I wouldn't object to it.)
Every solution so far is compatible with adding lookahead later and simplifying the syntax. And frankly, even if/when full lookahead ships, it might still be beneficial to also have an optional prefixed syntax for authors who want to make their stylesheets more clear (though I wouldn't be bothered if we didn't).
I am also interested in exploring the suggestion from @bramus about shipping a subset of option 3 with a required & to start, then gradually adding more functionality over time. That seems like a promising compromise, though it would likely make the
@supports
story much more complicated
I think you may be referring to the point I made: that if we know infinite lookahead is coming, I'd rather ship a syntax with a mandatory &
, so that it becomes optional for descendants and combinators at once. Note that I'm only proposing this for user experience, it does not solve the lookahead issue to have a mandatory &
.
Note that I only think this is a good idea if we know that infinite lookahead is possible. If not, I think Option 3 as it currently stands is a better bet.
@LeaVerou I was thinking of the addition to your suggestion that we have a mandatory & and only allow the nesting forms that begin with &
until we determine whether lookahead is possible.
One of the claims I have heard is that we can ship option 3 now, then in a lookahead future we can update nesting syntax. Could someone elaborate on what that update would look like?
For authors, the update looks like "you don't need to worry about selectors starting with an ident anymore, it Just Works™ (but if you were already doing :is(div).foo
that's still fine)".
For implementations, we're working thru what the parsing algo would look like in https://github.com/w3c/csswg-drafts/issues/7961#issuecomment-1396112293
I think you may be referring to the point I made: that if we know infinite lookahead is coming, I'd rather ship a syntax with a mandatory &, so that it becomes optional for descendants and combinators at once. Note that I'm only proposing this for user experience, it does not solve the lookahead issue to have a mandatory &.
I'm strongly against a "mandatory &" - nothing is helped by requiring an & somewhere. (That is, div > &
satisfies this but is still problematic in such a world, while > div
has no problems but is mysteriously disallowed by such a restriction.) I don't understand where this suggestion is coming from or in what way it would help us.
and only allow the nesting forms that begin with & until we determine whether lookahead is possible.
This is also overly restrictive. Again, if we determine infinite lookahead is possible, then div + &
is fine. If we determine that it's not possible, then the WG has already spent months talking over all the options, and decided the current spec is the most preferable way forward.
The only reason it would make sense to impose such a restriction is we think that, when we determine that lookahead is infeasible, we'll scrap all the preceding resolutions and re-evaluate the entire issue fresh. But we're not going to! All of the discussions we've already had were made under the assumption that infinite lookahead wasn't possible, so if we decide that it is, indeed, not possible, we'll just be affirming that the preceding discussions were assuming correctly.
The only effect that our investigation into infinite lookahead can have is proving that is is possible, and causing us to switch over to a parsing method with the new assumptions. Otherwise, our current situation is unchanged, and we should stick our current resolutions.
Here is my reasoning, which is not based on scrapping anything.
A. Start by allowing nesting forms with a mandatory leading & (so that people have something to ship now) B. Keep investigating lookahead C1. If lookahead is feasible, never do the :is() thing. When a UA can do lookahead, then they can ship the rest of nesting (and make & optional where possible) C2. If lookahead is not feasible, UAs can ship the rest of option 3
Right - how does that help authors? Either infinite lookahead is feasible, in which case they can write div &
(or :is(div) &
, tho they wouldn't), or it's not feasible, in which case they can write :is(div) &
. Disallowing them from writing either (and disallowing them from writing .foo &
, which they'd be able to do in either case) until we figure out which case it'll be doesn't seem to help anyone.
If it’s possible to avoid the :is(div) &
form entirely, I think that’s a win for authors. My preference would be to hold up shipping anything until we thoroughly investigate lookahead, but I expect some sort of compromise may be needed.
I'm strongly against a "mandatory &" - nothing is helped by requiring an & somewhere. (That is, div > & satisfies this but is still problematic in such a world, while > div has no problems but is mysteriously disallowed by such a restriction.) I don't understand where this suggestion is coming from or in what way it would help us.
I'm against that as well, but that's not what's being proposed here.
The only reason it would make sense to impose such a restriction is we think that, when we determine that lookahead is infeasible, we'll scrap all the preceding resolutions and re-evaluate the entire issue fresh. But we're not going to!
1) 'we're not going to!' isn't your call, that's the chairs' call, and other group members are well within their rights to ask that these decisions be revisited. 2) while all the previous decisions were made with the assumption that lookahead wasn't possible, they didn't take into account all the pitfalls of the solutions at the time. Yes Tab, I'm aware that you said that you did, and I accept that, I also accept that others involved in coming up with the solutions that led to the current option 3 syntax did as well, however those pitfalls were absolutely not part of the discussions with the whole group when the resolutions were made[1]. They also weren't mentioned in the public polls. So yeah, if lookahead isn't possible, we do need to revisit at least some of the previous decisions given the new information. If lookahead is doable, then that's all moot and there's no point revisiting those. Given the degree of contention, and emotions running high, it might be best to avoid going there if we can by simply waiting a few weeks for the lookahead investigation.
If it's determined that the lookahead research is going to take longer than people are willing to wait (which has not been determined yet), then shipping a partial solution that always requires some kind of prefix is the safest bet. Because if we determine that lookahead can't work, and if we revisit the prior decisions and decide that an optional prefix isn't actually the best path forward after all, then we haven't painted ourselves into a corner by shipping something prematurely. We can always make at least some of the mandatory prefixes optional, but we can never make optional prefixes mandatory without breaking content.
I also have yet to see any justification as to why this needs to be rushed. If there's a real need to push a solution out the door ASAP, then there should be justification for prioritizing the lookahead research work.
[1] for example, when I mentioned some of the language evolution tradeoffs, people were surprised.
If it’s possible to avoid the :is(div) & form entirely, I think that’s a win for authors.
I have a hard time imagining how that would be a win. Even if :is(div) &
is cumbersome, it is better than not being able to express that at all, by requiring the selector to begin with a &
.
If it’s possible to avoid the :is(div) & form entirely, I think that’s a win for authors. I have a hard time imagining how that would be a win. Even if :is(div) & is cumbersome, it is better than not being able to express that at all, by requiring the selector to begin with a &.
With lookahead that would simply be div &
, which is the win we're talking about. What @astearns is describing is a proposal where even :is(div) &
wouldn't be valid in V1, only selectors that begin with a &
(and have no other &
anywhere). Once we know if lookahead is viable or not, we either make div &
valid (and any other selector without a &
, leading or not) or :is(div) &
depending on the outcome.
@tabatkins The slimmed down option 3 – with required leading &
- was proposed during the call to break the standstill we were in:
With that slimmed down option 3, vendors could ship at least “something” (which could still be upgraded to the full Option 3) while also preventing the formal objection from happening. It also worked around other discussions that were still ongoing at the time (e.g. “How would it desugar?”).
Would this have been ideal to authors? Probably not, but at least it would have allowed them to cover only the basic cases.
But the entire suggestion is no longer valid I think, as @emilio offered a better escape hatch to the standstill by looking into the viability of infinite lookahead. As detailed in https://github.com/w3c/csswg-drafts/issues/8249#issuecomment-1396198517 by @plinss that research won’t prevent Option 3 from shipping. Correct me if I’m misunderstanding Peter, but it would boil down to:
So yeah, Option 3 is the version that’s still on the table and the one that will go out the door. Maybe it’ll even get an extra upgrade before it does.
I'm against that as well, but that's not what's being proposed here.
It was, by Lea, and I said that in a response quoting them.
If it's determined that the lookahead research is going to take longer than people are willing to wait (which has not been determined yet), then shipping a partial solution that always requires some kind of prefix is the safest bet.
The timing of the research has no bearing on what we do in the "infinite lookahead is infeasible" case; this is a non sequitur. The only way in which it can be relevant is if we start by intentionally limiting the current spec to the intersection of "infinite lookahead is fine" and "infinite lookahead is not fine". I don't think we should do so (as I said in response to Alan), but if we did, a required prefix is already out of that intersection.
The only reason to use a prefix now is if we are planning to revisit the last year of discussion, and think that a particular prefix syntax has a good chance of being the final resolution. Absent that, any prefix is just as uncertain/unsafe as any other syntax.
I also have yet to see any justification as to why this needs to be rushed.
"It doesn't need to be rushed" and "it doesn't need to be further delayed to revisit previous decisions, after more than a year of active discussion" are different things. If evidence of infinite-lookahead viability would cause us to present a different author-facing syntax, that would be a different story, but it won't.
Since the current syntax is compatible with either result of the lookahead research, the only reason to continue to delay shipping at this point would be if we thought we were going to overturn our existing consensus. Whether we do so or not has nothing to do with the lookahead research, however.
The slimmed down option 3 – with required leading & - was proposed during the call to break the standstill we were in:
I know, but again, that wasn't what Lea was referring to, either here or in the call. They explicitly said they were talking about requiring a mandatory &
somewhere in the selector, separate from whatever we do for the prop/rule disambiguation. (This sort of restriction has been talked about in previous discussions.)
Giving option 3 the stamp of approval would have resulted in a formal objection because we lacked data about a relaxed/infinite lookahead being viable or not.
An important point that keeps being overlooked is that Peter has said they'll object to the current spec (Option 3) after the lookahead research, too. The only approval they've given to the current spec is as a stepping-stone impl to infinite lookahead, but very specifically not as an alternative if infinite lookahead is unviable. There is no future in which the current spec stands and Peter doesn't object, at least given their stated opinions over the last few meetings.
I'm strongly against a "mandatory &" - nothing is helped by requiring an & somewhere. (That is,
div > &
satisfies this but is still problematic in such a world, while> div
has no problems but is mysteriously disallowed by such a restriction.) I don't understand where this suggestion is coming from or in what way it would help us.
Ok, let me explain a bit more.
Being able to skip &
for selectors that don't start with an ident, is a tradeoff: we win portability (better/copy paste between nesting and @scope
) and efficiency (faster to type and arguably faster to read) but "you just have to start every nested rule with a &
" has far better learnability. Unlike some of the opponents of Option 3, I don't think the rule is incomprehensible to authors (heck, you may recall that Option 3 was originally called "Lea's proposal" 😁), but "every rule has to start with &
" is certainly easier.
Note that div &
can just be rewritten as &:is(div *)
, so expressive power is the same for most (all?) cases.
So I'm tending to somewhat agree with @plinss and @astearns here: if we can ship the better syntax once the unbounded lookahead investigations have finished, I'd be fine shipping a more restricted, easier to explain, form now.
As mentioned I'm opening a new issue to outline the objections I raised in the call today and to discuss paths forward.
My primary objection to the current syntax is that mixing properties and selectors without a clear distinction places unacceptable (to me) restrictions on the future design of the CSS language.
An implementation parsing a style declaration will need to presume anything that doesn't start with an identifier or function is a selector, which restricts our ability to ever extend property declarations with anything that doesn't start with an identifier or function. Furthermore this restricts our ability to ever extend the definition of an identifier or a function. As an example, if this were implemented first, we could never have added custom properties with the current syntax (which redefined identifiers).
Alternatively, we could limit selector combinators to the current set and a limit extension path, like
/<name>/
, this would place restrictions on future selector syntax and potentially add more confusion as to the rule of when a&
or the:is()
hack is required. Not a fan of this.I see two paths forward (and welcome other suggestions):
1) We remove the lookahead restrictions on the parser and 'simply' adopt the SASS syntax. The lookahead restriction came about 25 years ago when there were real concerns that CSS would be too slow to ever implement in a browser and everything was focused on performance. I'd like to see some experimentation and real-world data to check that assumption and see if advancements in processor speed and RAM availability allow us to relax that.
2) We add something that clearly distinguishes selectors from properties within a style declaration.
Something like:
is fine by me, but I accept that this has been proposed in the past and rejected.
A compromise I'd be OK with would be treating a bare
@
inside a declaration as the equivalent to@nest
(and possibly allowing@nest
to be optional for those wanting clarity). This is functionally equivalent to requiring the&
(which many people in the polls preferred), but also handles cases where the&
isn't the start of the selector without adding lookahead. e.g.:This leverages the fact that
@
is already CSS's universal escape hatch and clearly distinguishes properties and selectors, allowing unrestricted extensions of either in the future. It also minimizes verbosity as the majority of nested selectors can simply start with an@
and requires no other changes or special rules to learn.