Hacker News new | past | comments | ask | show | jobs | submit login
Help pick a syntax for CSS nesting (chrome.com)
217 points by feross on July 27, 2022 | hide | past | favorite | 240 comments



I see that many didn't read the hidden part of the post that explains why SCSS version can not be used.

Here's the relevant excerpt.

> ## Why can’t everything be directly nested?

> Nesting style rules naively inside of other style rules is, unfortunately, ambiguous—the syntax of a selector overlaps with the syntax of a declaration, so an implementation requires unbounded lookahead to tell whether a given bit of text is a declaration or the start of a style rule.

> For example, if a parser starts by seeing color:hover ..., it can’t tell whether that’s the color property (being set to an invalid value...) or a selector for a <color> element. It can’t even rely on looking for valid properties to tell the difference; this would cause parsing to depend on which properties the implementation supported, and could change over time.

> Requiring directly-nested style rules to use nest-prefixed selectors works around this problem—an & can never be part of a declaration, so the parser can immediately tell it’s going to be parsing a selector, and thus a nested style rule.

> Some non-browser implementations of nested rules do not impose this requirement. It is, in most cases, eventually possible to tell properties and selectors apart, but doing so requires unbounded lookahead in the parser; that is, the parser might have to hold onto an unknown amount of content before it can tell which way it’s supposed to be interpreting it. CSS to date requires only a small, known amount of lookahead in its parsing, which allows for more efficient parsing algorithms, so unbounded lookahead is generally considered unacceptable among browser implementations of CSS.


Thanks for pointing this out, I indeed missed that (it's hidden behind a folded caption).

It still doesn't explain why all the proposals feel the need to be more powerful than SCSS nesting (allowing "reverse nested" definitions where the element being nested in is not at the start), but it does explain the need for "&".

EDIT: Actually, it looks like SCSS supports the same thing by using '&' anywhere in the definition. I'll just stop posting since it looks like my knowledge is too rusty to contribute positively.


I’m not sure if I understand you correctly, but I think SCSS does actually support that.

    dialog {
      html:has(&[open]) {
          overflow: hidden;
      }
    }
Is possible annd creates html:has(dialog[open]) {overflow: hidden;}


No, you're right. My SCSS is a bit rusty these days and I simply forgot how flexible it is, sorry for the misleading comment.


This seems to slap the property on html, whereas the proposed nesting only uses the parent as a selector, and specifies properties for the nested element (if I'm not confusing something again).

If scss can do `.parent & {...}`, that would be the same as the proposal.


Thanks, I couldn't get it through the link since it's behind a "text fragment" link which only works on Chrome[1], a bit ironic if you ask me when we are discussing a standard :)

[1] https://caniuse.com/url-scroll-to-text-fragment


Works for me in Firefox. It seems to just rely on a <details> element [1], which should work in all modern browsers [2].

[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element/de... [2] https://caniuse.com/details


I am not talking about the accordion, I'm talking about this link:

https://www.w3.org/TR/css-nesting-1/#:~:text=Why%20can%E2%80...


It's accessible on any browser, you'd just need to scroll to the section manually if your browser doesn't support text fragment links.


You know what is accessible on any browser that they could have done? Proper fragment links from HTML 1.x such as:

https://www.w3.org/TR/css-nesting-1/#nesting

The W3C document template even kindly provides a quick way to copy those links to every header using the section mark (§).


I needed to scroll to the section manually since my browser doesn't support it as shown clearly in the caniuse link I shared. It doesn't work for me and I provided general evidence, so how is it accessible on any browser?



what's also silly is using qualtrics for a single-question survey that has 5 screens, including the google captcha surveillance tracking injection.


I assume that the requirements for a browser implementation are more strict than for existing pre-processors such as Sass because 1) the performance budget is much more limited for interactive use compared to pre-processing and 2) browsers need to handle adversarial inputs gracefully. Are there any other reasons?


One potential reason is, SCSS only needs one implementation, and whatever that implementation does is SCSS. If you write `color:hover { ... }` it doesn't really matter whether SCSS happens to parse that as a `color: hover` followed by an illegal `{` token or if it happens to parse it as a nested selector; whichever the parser chooses is fine. But in a standard, with multiple implementations, you need to strictly resolve all ambiguities in the spec so that all implementations parse CSS the same way; just avoiding ambiguities in the grammar is by far the easiest way to achieve that.

Plus, different ways of implementing a parser could have different ways of resolving ambiguities. Maybe parsing `color:hover {}` as a selector happens to be super easy in Firefox's existing parser, but would require a rewrite of Chromium's parser, for example.


The color:hover example seems like a pretty wild edge case. I'd be interested to see a more solid example of a 'boundless' lookahead requirement. In my simplistic view surely a strict mode that requires semicolons after properties (as opposed to commas or curly brackets after selectors) would do the trick?

Edit: I put together (painfully typed out on a phone) an example regex[1] to use the color:hover example. It isn't perfect, but it also isnt boundless.

[1] https://regex101.com/r/aOc2Pz/1


The problem is not to tell selector from definition (of course it can, otherwise grammar would be non-reducible). The problem is how far in an array of tokens the distinction can be seen.

Parsers know the current state and where to go next out of previous reductions and a few lookaheads which they can use at places known to be clear of ambiguity in a ~fixed number of lookaheads. Parsing something that diverges only down few pages of tokens is problematic size/speed/algo-wise, but the question is how much does that cost really and who should pay.

Edit: But honestly I think this is all wrong direction. Any technology, format, method has 1. features and 2. form. Form is irrelevant because you can make tools to convert any form into any. Features are important because you can’t add features by tooling. But $subj adds zero features, only a new form which is already feels like a compromise. Why do that then? We have to brush our tools instead, e.g. create default nginx-scss plugin, standalone scss caching servers etc. Then everyone could use extended syntax without resorting to all-in-one monstrosities like webpack.


> Form is irrelevant because you can make tools to convert any form into any

No, form is not irrelevant.

People don't write bug-free code. The form has to work reasonable well even when there is a syntax error in the code.

C++ templates is a main example where this often goes hairwire.


Next { or ; is not few pages down.

This all seems like purely academic objection that in practice could be made equally fast.


Consider this code:

color:hover .foo .bar .qux div span

You have no idea whether to parse that as a property (setting `color` to that string) or as a nested selector (a span below a div below .qux below .bar below .foo below a color element that's being hovered over). You only know that it's a property if you see a `;` or a `}`, or a nested declaration if you see a `{`.

So yeah, the ambiguity might not be resolved for a pretty long time. And, unless there's some aspect to CSS's grammar I'm not thinking of, this ambiguity exists for every single property (even for stuff like `background-color: blue` -- the parser shouldn't know that `background-color` isn't an HTML element or that `blue` isn't a selector).

It really wouldn't surprise me if adding unbounded lookahead to every single CSS property would cause a pretty significant parsing slow-down.


I personally trust the feedback from the actual maintainers of such performance critical parsers.


Yep, that’s why I find it questionable too.

Next { or ; is not few pages down

A little nuance is that when it is, we either want to make that illegal or to process this case anyway. Both ways open a next can of worms.


I guess, if you want to write slowest CSS file on the planet by cramming together three pages of selectors before you open a bracket ... be my guest.


Yeah this is my intuition as well, though I haven't spent much time thinking about the problem. Like, yeah, if you write very non-performant code, it won't perform well. So what? Don't write stupid code.


The issue is that the person who ends up suffering isn't the developer that writes the CSS. It's the person who has to use the website.

If I'm trying to find a local emergency room, I really don't want to have to waste time because a developer wrote (or some tool generated!) slow CSS.


I had hired in at a new company and was getting annoyed with how slow everything was so I started digging into the Javascript and CSS.

I started seeing a lot of references to Chuck Norris.

After further digging, found that the entire test library suite was being added to code.

Site load time was halved after fixing.


To understand why this is a problem even with “good” css, one has to understand how parsers work. It doesn’t matter that all css will be good in practice. The entire theoretical possibility of a problematic case (i.e. unbounded number of lookaheads) changes a class of parser required to parse it. Not all grammars are equal and they may require different methods. One grammar may be LALR(const) compatible, and the other may be parsed only with GLR. There is no easy way to use a dumb and quick parsing method once you commit to a grammar (not to a way of writing sources!) in which ambiguity can survive for an indefinite time, even if it’s always short in practice.


I guess I'd just like to see a good example. CSS is fairly process intensive, not to parse, but because it directly effects the render. Surely compiling a sheet with some lookahead magic isn't going to explode anything that you couldn't already explode with a 'will-change'?


Wouldn’t that break compatibility with existing websites?


Not if you only require strict mode for modern language incantations like nesting


Then you’d have two diverging CSS dialects, which would add complexity, too.


Hacking around “wild edge cases” is how we get impossible to implement standards.


How does the SCSS syntax differentiate between `& .childclass` and `&.additionalclass` without the ampersand?


It doesn't. If there is no ampersand it is assumed to start with `& `. So `.childclass` is equivelent to `& .childclass` and if you want something else you need to do `&.additionalclass` or `& + .additionalclass` or whatever you want.


Are CSS parsers multithreaded? Maybe they should be? Maybe then a bit larger lookaheads woulndn't matter?


It's not a question of implementation power. It's about spec requirements. Until now CSS didn't require more than 1 token to know where we are. Basically, parser doesn't need to backtrack at all.

SCSS apporach, for instance, requires look ahead.

Consider: color:hover over some random markup &

Currently CSS can be sure whether that is a selector or a property depending where it finds it. If it's in a ruleset or at-rule block, it's a selector. If it's in a declaration list, it's a property-value pair.

Now, with nesting it becomes confusing. Parsers can not rely on the state to decide what they're looking at.

One way to fix this is, like many suggested here, to require look-ahead from parser. Which can be a solution in a well defined environment. Unfortunately, the Web is not one of them. We have many underpowered kiosk-type devices that probably can not spare arbitrarily large look-ahead buffers.

Another solution is to somehow let the parser know what it's looking at. That's why leading & is a good indicator it's a selector. & is not used for anything else in CSS. But for selectors where it's not first we need @nest (or anything else that unambiguously marks a selector).

Look-ahead requirements are bad for another reason, too. It makes CSS platform-dependent. Currently any version of CSS clearly states what it provides. Implementations can be partial but they can not claim they support CSS 2.1, for example, when they don't implement 100% of it. Wit look-aheads things become murkier. An implementation can support 100% of the spec but depending on the look-ahead required to parse a stylesheet they might not work. Currently, I don't think there are any CSS features that depend on platform limitations, at least not for parsing. Requiring a look-ahead buffer to parse a stylesheet can be very problematic. Imagine, Twitter got hacked and their embeds started stylesheet have a couple selectors that require 1M tokens look-ahead buffer. How that would break every site that embeds tweets?


I feel like one could just split the css around (uncontained) } and hand off each portion to microthread.

Basically do it by multithreaded recursive descent.

Then what on the left of { is a selector and what's on the right are the properties.


Parsing is a really difficult-to-parallelize task. There's no obvious simple way to chunk up the task, since a `{` at line 1 might affect the parsing of a construct on line 100000.

What you could conceivably do is to have a very simple parser whose only job is to have just enough understanding of CSS to be able to split the CSS document into chunks, and then have a proper parser which works in parallel on the chunks produced by the first parser. I can't imagine this would be faster on most code though, considering how simple the full CSS parser is today, and it still requires a single thread which scans through and parses the entire CSS document.

Regardless, browsers are already really multi-threaded. When you parallelize parsing in this way, even if parsing gets a bit faster in isolation, you're increasing the amount of computation necessary to parse the CSS (i.e spending 1 second on one core is less computational work than spending 0.5 seconds on 4 cores). Your parallel CSS parsing will be taking cycles away from HTML parsing and javascript execution.


Doesn't the :has() selector already cause unbounded lookahead?


More efficient how much?


No way to measure that because the potential inefficiency is infinite - one would need to first work out the likelihood of very large unbounded lookaheads occurring in the wild.

It's really less about benchmarking and more about managing risk: large unbounded lookahead in scss might crash a single concurrent pod in your CICD - isolated, detectable and really easily remedied. The same here could crash many users' browsers - not so easily fixed.


Can you elaborate exactly where the unbounded lookahead is required? I genuinely can't see it. I also don't see why a dramatic update like this couldn't also introduce / require a 'strict' style to be considered valid.

I feel like we're talking serious edge cases here.


Do you want a new attack vector to make your browser crash? It's not like all CSS is crafted benevolently!


malevolent css targeting non-performant regex .. sounds utterly despicable.


> Can you elaborate ...

I'm not sure I can think of a better explanation/examples than the gp. It essentially comes down to ambiguity between single element selector names (which are unprefixed in CSS - no dot or hash) and property keys (also plain identifiers).

The only similar nested braces form we have in current CSS is always @-prefixed. There's no unprefixed equivalent for parent blocks in current syntax (plus those blocks are currently limited to a single level which I guess mightt simplify things further given the prefixed selector is always at root level)

> I also don't see why a dramatic update like this couldn't also introduce / require a 'strict' style to be considered valid.

I think it could and I think the call for input is open to that as a possibility. The above comment is just referring to a fully compatible direct scss implementation of nesting.


I get your point. However it relies on an example that actually requires an unbounded lookeahead. This is where I'm confused. Sure, these ambiguous blocks (I'm not sure how many examples there are other than color:hover) aren't prefixed, but they are conclusively postfixed. So any lookahead required should be easily bounded. Where does the unbounded aspect come into it?

Reposting my earlier painfully phone-typed regex to illustrate the point: https://regex101.com/r/aOc2Pz/1

Edit: I should add that I don't doubt that those involved know what they're talking about. I know nothing of briwser-level CSS parsing. I am certain it is a nightmare. I would really just like a better example to explain the unbounded aspect. Especially when it is being given as the primary excuse to cripple / greatly obtusify a syntax I'm being asked for an opinion on. Maybe it's just me but I would expect that an evolution of this magnitude would afford a bit more compromise elsewhere.


I think when we talk about "unbounded lookahead," it means the lookahead can't be bounded by any constant. A common type of parser is called LR(1), meaning it can only lookahead by 1 character.[0]

The advantage of only looking ahead by a constant is that parsing can be done in linear time. For a backtracking parser, you have to look ahead by up to n characters each time, leading to a quadratic runtime in the worst case.

[0] https://en.m.wikipedia.org/wiki/LR_parser


Thanks for the explanation/link, appreciate it. I'll reserve my cynicism of using this as a reasoning to hold-up progress.


You've just turned this one spec proposal into two! I thought this was about simplifying things, not doubling the amount of work.


you've got to break a few eggs..

Seriously tho it seems like a pretty major feature to be introducing. Why not do it right? Surely the complexity should be concealed rather than out front being dealt with in user space.


Choosing only among the given proposals, what I see is one clear, concise and reasonably elegant syntax, and two ugly and noisy ones. Frankly it vaguely feels like the two latter options are there just to shepherd the ‘community’ (if there's such a thing) toward the least egregious variant and offer a semblance of discussion. It's not even necessarily bad that there's one obviously best option, if it's dictated by technical constraints—just, why bother me with the other two?

If they're seriously considering the latter two options for us to be stuck with, that's very concerning, and I'll be nervous about the whole affair until something definite rolls out.

Of course, it might be that the syntax authors foresaw some future problems that I don't, but their arguments aren't provided.

In fact, I don't even see why the `@nest` marker is necessary at all. The W3C draft says, “Some valid nesting selectors, like `.foo &`, are disallowed”—but why? They introduced the `&` marker to tell nesting rules apart from anything else, but then it turns out that they can't do that with the marker either? See `&` anywhere in the selector, realize it's a nesting—how much lookahead is needed for this? `&` can't be in a property name or media rule, can it? Surely there's a reason for this constraint, only I can't figure it out—granted, I barely know anything about implementing a CSS parser.

(Update: text cited by @pointlessone notes that a nest-selector might look like `color:hover & something`, which is indeed quite wacky and apparently befuddles the parser.)

Also, philosophically, a more-verbose marker like `@nest` would help avoid the growth of ascii salad in the syntax (the way that Python and Lisp avoid it, compared to Bash and Perl)—specifically if the marker was used instead of both `@nest` and `&` together in those proposals. However, it's probably not such an issue in regard to a feature that's gonna get plenty of exercise and is practically core functionality.


Take a better look at example 5: [1]. In that case, the `@nest` marker is necessary for proposal 1. Proposal 2 is saying, if we need @nest some times, why not always make it required, for consistency. In that example, I'd say proposal 3 is the least ugly.

However, it's unclear to me why we want to allow the & marker to appear anywhere in a rule, and not just at the start. Every time I've used nesting in a CSS pre-processor, I've always added to the selector at the end.

[1] https://developer.chrome.com/blog/help-css-nesting/#example-...


The example by itself doesn't explain why I can easily grok that `.parent &` is a selector with nesting, but the parser can't. The `&` marker is introduced for nesting in the first place, so adding another one to specify that it's definitely hundred-percent pinky-swear artisanal nesting looks plain dumb.


It requires unbounded lookahead in the parser.


I got a reply asking why this is, which either got deleted or isn't showing up for some reason. The reason is, before nesting, you know based on context whether something is a selector or a declaration. Top level / inside a media query, it's a selector. Inside a selector, it's a declaration.

With nesting, but without the prefixed @nest, you have backtrack after arbitrarily many tokens if you see a & and need to switch from parsing a declaration to parsing a selector.

E.g.:

    div {
      div:hover div /* 4kb of div */ & { }
    }
Note that the colon makes it a valid-looking declaration (property `div` and value `hover div...`), until you read all the way up to the &.


Yeah, it's all quite simple when thinking for a bit on a clear head. For some reason I didn't realize at first that the whole ‘:hover’ problem only pops up when nesting is introduced.


> if we need @nest some times, why not always make it required, for consistency

Prefixing a parent selector seems super ugly. The facility is available in SCSS, but I suspect it is not used much (e.g. comment above not knowing the feature existed).

Using & for the usual case of appending selectors seems sensible to me.

Aside: I suspect using & to avoid lookahead parsing is important for efficiency and to avoid DoSing the browser (especially malevolently).


Regardless of the merits and feasibility of implicitness vs. explicitness, I seriously question whether it's ever a good idea to mix the two. That's just asking for confusion exactly in those cases where selectors are confusing already; i.e. adding complexity where it hurts most.


> `color:hover & something`

Though then again, I still don't see why the parser can understand that `color:hover something` is a selector, but can't understand that `color:hover & something` is a selector with nesting. I don't need to mark a shitty selector like `color:hover` with special markers for shitty selectors, do I?


Update: it finally dawned on me that only with nesting things like `color:hover` can appear where both properties and selectors are expected. Apparently today is not my thinking day.

Still would like to understand exactly how ‘unbounded lookahead’ arises.


Lookahead is how many tokens the parser needs to see before it can make a decision what branch it is in and start ingesting those tokens properly.

In `& color:hover` you've got at least four tokens (making some basic assumptions about how the tokenizer works and ignoring whitespace):

    1 &
    2 color
    3 :
    4 hover
In this case you've got that `&` in the first token and the parser knows what to do with that (only appears in nesting scopes). (No lookahead needed.)

In the case of `color:hover &` the situation reverses:

    1 color
    2 :
    3 hover
    4 &
Tokens 1/2/3 are ambiguous (could be a property:value or could be tag selector:state) and it isn't until token 4 that it becomes obvious that it must be a nested selector because that & token is the disambiguator. So here you need a lookahead of 4 tokens just to disambiguate.

The problem is "unbounded" because the way selectors and values work they can be arbitrarily long. You can have "long property values". This doesn't make a lot of sense in current CSS, but is a valid property value assignment:

    color:hover, tt, .frankenstein, p;
This is a possible (weird) nesting:

    color:hover, tt, .frankenstein, p & { }
The number of tokens needed to lookahead is "unbounded" because you can keep adding tokens to that weird property value or to the nesting example with no clear limit.


Thanks! In fact I easily see this myself now that I've gotten some sleep. Really can't recommend sitting at the computer for hours.


I also found the second and third options unnecessarily noisy. Though, I'm not surprised with the obvious syntax warts being proposed considering the disaster that is `globalThis`.

I do hope the first choice is the one used as it is the most reasonable while working around existing CSS parsing rules.


I think option 1 is the most elegant, but I also realize that as a user of Less and Sass it is the most natural choice for me. The `@nest` having to appear when the ampersand exists after any other character in the line is an okay inconsistency for me, since I rarely come across that form and when I do it it's in short bursts with tightly coupled selectors. You can also think of it as a warning sign for yourself that you have an ampersand in a not-so-conventional spot in the line. If option 2 happens I wouldn't be disappointed, either. The brackets example, however, just looks wrong to me.


Honestly I think "@nest always" makes the most since since it would be consistent with @media and unmagic the leading '&'. It gives @nest the feel of printf.


But the example with multiple selectors and & at the start is unclear due to the precedence of the @nest operator over the comma:

    /* @nest always */
    @nest & + .baz,
    &.qux {
      color: red;
    }
It's clearer perhaps with different nesting, but that's fragile and a pain:

    /* @nest always */
    @nest & + .baz,
          &.qux {
      color: red;
    }
At least with option 1 you only end up with this issue in the uncommon case of multiple selectors and a non-leading &.

    /* @nest */
    & + .baz,
    &.qux {
      color: red;
    }


yah, i have a strong preference for the first option, and don't mind that it requires using an & token relative to scss that requires none.

the @nest always option seems overly verbose, and adding more brackets visually overloads them in a way that makes it harder to parse quickly when scanning.


[flagged]


The post makes it quite clear that all three proposals sprinkle some markers and constrain their use to avoid ambiguity for the parser, so not sure what you're talking about.


The more markers you sprinkle, the less ambiguous the grammar should be.


nesting them, if you will


If i read this right, the only reason we can't just use & anywhere in the selector without some funky @nest indicator, is that browser vendors don't want their css parser to have unbounded lookahead just to figure out if something is a selector or a property.

I gotta say, this strikes me as rather developer hostile. Now, developers have to appease a crippled parser by injecting @nest (or a billion brackets) strategically. The amount of devs that are going to have to scratch their heads at each of these three weird proposals is way more than the amount of devs building CSS parsers, so I think they don't have their priorities straight.

I mean, in the 99.9% case, nested selectors are going to be at most some reasonable amount of tokens long. I understand wanting to avoid too many heap allocations but if that's the only issue, would it be that big a hack to first try to parse it with a bounded (but reasonably long) lookahead, and just reparse the whole thing in "insane CSS" mode with mallocs all over the place if the bounded one errors out? That way virtually all sites keep the fast small CSS parser and if some web dev really much challenge the engine with obscene code, it's supported but slow. They do this all over the JS engine, why can't CSS get a teeny tiny little bit of that attitude if it means CSS stays easy (enough) to learn?


Stuff like that is, however, complex. Complexity breeds both slowness, expense to implement, and security risks (at the very least DoS risks). It may also require a pretty significant rearchitecture of existing css parsers, which will hinder adoption. It also makes future competing browser engines that little bit less likely.

There's a bit of tradeoff here of devs vs. users.

But regardless, I think the implicit syntax (as is) is clearly the worst anyhow - because it's important even for devs to avoid syntax with gotcha cases. Unfortunately the optional/implicit style can't always avoid @nest, and precisely in more complex cases it is required. That makes those even more confusing; I'm not a fan of adding complexity precisely where it hurts most, even if it's a little shorter in some cases.

Had the implicit syntax _always_ allowed omitting @nest it'd be a different story, that's clearly best, but if browser-manufacturer's refuse that option, well, then let's not make it optional only in some cases.

Personally, I'm not sold that this is a great feature at all, because nesting encourages high selector specificity, and tightly coupled details of the dom structure with css. That's still a recipe for specificity battles and refactoring gotchas, no matter the syntax. Nesting is a dangerous feature because it's pretty attractive superficially, but leads to pain way later in the development process: not good.

In some niche cases (e.g. for pseudo-elements or media queries) there's never a maintainability problem with nesting, and hey, those are some of the cases where there's no parser ambiguity either, even with the implicit nesting syntax! I'd rather CSS restricted the nesting feature to those sane choices, than repeat SCSS's mistakes.


It's not just browsers. Other tooling will also benefit from a more explicit and less ambiguous syntax - think e.g. code completion.


These all feel like an overengineered solution in search of a problem, with some degree of tunnel vision in that all three proposals try to support the same broad set of capabilities.

It looks like somebody decided that nesting support must somehow support "reverse nesting", eg. that you'd like to define what happens to .foo when it's inside a .bar, but nested inside the definition for .foo:

.foo { .bar > .foo { ... } }

Does anybody actually want this? It feels counter-intuitive and weird, and with the syntaxes proposed, seems to provides little benefit compared to just doing it as the old ".bar > .foo" without nesting.

If you drop the requirement for nesting to work in reverse, you can just go with the proven SCSS syntax which is extremely clear, straightforward to use, and requires 0 extra characters typed.

EDIT: Apparently the '&' or some other special token is required to prevent the need for unlimited lookahead while parsing. This is actually explained behind a folded caption in the article, which I initially missed.


To play devil's advocate: there's a case to be made that reverse nesting is precisely the the _most reasaonable_ case for nesting, and that forward nesting is the one to be avoided.

The problem with nesting is that it is seductively easy to write, but encourages two coding issues that cause maintenance headaches later on. Firstly, it encourages high selector specificity, and that means potentially enjoying specificity battles later on. Secondly, it encourages tightly coupled DOM structure with CSS - and tight coupling makes later development more expensive.

Both of these issues are significantly less serious when using reverse nesting than forward nesting.

Forward nesting allows for expressing the concept of "parts of a component" by it's DOM structure, so when the component is tweaked, selectors that aren't explicitly tied to any part of a component suddenly break. But reverse nesting is more natural to use in the context of expressing a special case; i.e. my thingmabob is generally green, but in this specific context it's grey.

So on coupling, reverse nesting is less risky: whil it's not impossible to "compress" your css and thus introduce coupling between component structure and a reverse nested selector, it's not a very natural mistake to make.

And on specificity too, reverse nesting is less likely to cause surprises - after all, since you're expressing a more specific situation, you're almost always likely to want the nested selector to win that specificity battle.

So, I'd argue that reverse-nested selectors are actually one of the few general class of nested CSS selectors that are unlikely to cause maintenance headaches later on. Similarly harmless are nested media selectors (again, because nesting implies expressing an exception), and nested pseudo-elements (because there structure and CSS are intrinsically coupled an expressed within the CSS anyhow).

What you call "reverse nesting" is actually the only best good kind of nesting. QED?


Just piping up to say yes, absolutely want the ability to put the `&` anywhere. Your example isn't exactly correct, what I use it for is adjusting styles of the element I'm referring to based on a parent class. The `&` isn't automatically prepended - it puts the element reference in the spot it sits in the CSS. In your example, the `.bar` would be the parent class that is adjusting the styles of the current element.

For: `@nest .bar > & { ... } }` (just using the first syntax for simplicity here)

That doesn't translate to: `.foo { .bar > .foo { ... } }`

It's actually just: `.bar > .foo { ... } }`


Apologies if I misunderstand, but your "reverse nesting" syntax already works with scss. I.e. `.a{ .b & {}}` => `.b .a {}` ... I'd quite honestly use it fairly often, usually with state/data modifiers on an ancestor (or ancestor sibling!)


Perhaps it was my own CSS preferences speaking, where I try to limit "spooky action at a distance" for consistency. But yeah, a".selected" state on an ancestor seems like a good use-case for this if you have different tastes. I stand corrected and see how that's useful.

I assume that for the same reason that they cannot support direct nesting with no extra tokens (parsing issues), they also cannot support the simple SCSS version and instead went for the "@nest .b & {" mouthful.


It's a mouthful alright!


Looks useful to me in order to accommodate parent modifiers classes.

.button { @nest div.small-print & {

a { @nest :not(nav) & {

Including the body:

.button { @nest body.has-js & {


This is a flawed approach and leads to N:M combinatorial explosion in stylesheets. Sure, it’s convenient to have component styles react to the context they’re in, but CSS Custom Properties are a much better solution for that.


> a { @nest :not(nav) & {

FYI, this `:not(nav) a` doesn’t do what you almost certainly intended. It doesn’t match “an <a> that is not inside a <nav>”, but rather “an <a> that has at least one ancestor that is not a <nav>”. You can’t currently express what you wanted there; the best you can generally manage is if you can anchor it with a parent selector, e.g. `:not(nav) > a`, which will match any <a> whose immediate parent is not a <nav>. I can imagine something along the lines of `a:not(:has(nav :scope))` working in the future too.


The @media application of this seems useful, but yeah arbitrary parents seems not a must-have.


Apparently, those already work differently than other selectors in the proposals (example 7):

  .foo {
    display: grid;
    @media (width => 30em) {
      grid-auto-flow: column;
    }
  }
I would expect "@nest @media (width => 30em) & {" for consistency with how it works everywhere else, but apparently @media is already special anyway in the "@nest" proposal.

I agree that this syntax could be convenient, though I'm actually a fan of having all the "@media" changes in one place, which is what the current syntax forces.


The cat is out of the bag but, one fear i have is how easy it is to abuse nested selectors this way. When I first was exposed to SCSS I also started seeing selectors like `.helpPage .faqSection .sidebar ul li` in lots of codebases I touched because it seems so neat and tidy in SCSS and made it easy to make that CMS generated list look just the way you want it to without injecting local class= attributes. Then 6 months later you want to restructure your containers and all your magic selectors work differently. Deep nesting is a trap those of us who have tried to maintain SCSS eventually learn and we use all sorts of naming conventions (e.g. BEM) to avoid it but for beginners having it there without tooling will probably mean we see even more of it in the wild at first.

Edit: I still voted for `@nest` -- CCS is a pretty complex language for sure but I think it's terse expressiveness is why it's been successful.


I agree in regards to people abusing selectors, but I see that kind of code even without nesting. People don't understand (and IMO don't bother to learn) CSS specificity. Then they wonder why their CSS falls over.


Perhaps I simply don't see the ambiguity after having used Scss, Less and the like for a long time, but isn't the simple `&` by far the best option here? The other two are absurdly verbose without offering any real benefit.


The simple '&' requires infinite lookahead to parse.


Afaict the lookahead problem refers not to the proposals, but to a syntax without these extra markers altogether. Which is why all the proposals have some markers.


So what? That's a complete non-problem.


no it doesn't, the simple no-prefix solution requires infinite lookahead


IMO this is one of the things, like TypeScript, where transpiling before hitting the browser is good enough considering the downsides of baking this into three web engines.


Well these all suck.

Obviously `@nest always` and `brackets` are the same in disguise, and are here cause they're conceptually cleaner.

The problem with both of these is that they're unnecessarily verbose.

Why not have a shorter token than `@nest` and something that doesn't want more indenting (like `{}`)? For example `\`? And then if you want to make things a little less verbose, just assume the first character is going to be `&` cause that's going to be 99% of use cases.

And then you get something like this:

  foo {
    color: blah;
    \ h2 { font-weight: bold }
    \ h3 { font-weight: bolder }
    \ i > & { ... }
  }
But no one ever listens to me!


Isn't that effectively what you get with the first option (with the `&` character)? You don't need `@nest` unless you want to embed the ampersand as anything other than the first character (which, I'll admit, is one of my major use cases). But given the prevalence of @ elements in CSS already (like @media for example), I'm ok learning/teaching @nest for those specific cases.


For me, the "equivalent css" is the most readable form in most of the examples given.

I can understand how this would be useful in some situations. However, from a practical perspective, I've found that writing nested CSS ends up creating a tight coupling between DOM structure and style rules which makes everything more brittle and harder to refactor [1].

I've personally been spoiled by the scoped styles in Vue [2] which makes this style of isolation in CSS mostly unnecessary. It serves a similar purpose as related encapsulation ideas like shadow DOM, BEM, CSS modules, and styled components.

[1] https://csswizardry.com/2012/05/keep-your-css-selectors-shor...

[2] https://vue-loader.vuejs.org/guide/scoped-css.html


Yes, I cannot agree more. I did the survey in the vain hope that I could leave a comment or even vote for “no nesting”, i.e. “equivalent CSS”. Most of the given examples are great evidence – some concerningly great evidence – for Tim Petersʼ “flat is better than nested”:

(1 and 2:)

  .foo {
    color: red;

    @nest .parent & {
      color: blue;
    }
  }
(3:)

  .foo {
    color: red;

    {
      .parent & {
        color: blue;
      }
    }
  }
(Without nesting:)

  .foo {
    color: red;
  }
  
  .parent .foo {
    color: blue;
  }
The nested variants, for my sense, foreshadow grave technical debt – in specs, docs, browser code and CSS files.


None of them honestly.

All of them are atrocious to type and parse for a human.

The option 1 is probably the best but urgh


I think option 1 is the worst, because it's not actually optional; it's required precisely when css selectors become complex. That the kind of gotcha that just makes everything harder to explain and read.

I hope the proposal fails entirely, and that no nesting syntax is adopted. It's not worth it, and nesting in SCSS is abused more often than not anyhow.

There are a few exceptions like pseudo-elements and media queries that are always harmless, but most nesting is just asking for specificity gotchas later, or component refactoring surprises.


All of these are stupid. Why can’t we just have the scss syntax? It’s already proven.


Naive question: doesn't it have to be something old browsers will ignore rather than misinterpret or fail to parse?

The 3rd party CSS tooling can pick whatever syntax it wants because it is starting on a green field. Browser probably can't?


The article answers this exact question already. It isn't possible because there's no build stage like preprocessors have to remove ambiguity.


If the preprocessors can remove the ambiguities, so could the browsers. If the concern is speed, then just stick with the status quo (my preference, but sadly not an option in the poll).


But why? Why does it HAVE to be the existing syntax or nothing? If we know that this syntax would make performance worse, why is an alternative that doesn't do so a worse option than not having this useful feature?


> But why? Why does it HAVE to be the existing syntax or nothing?

Because time and again userland implementations of useful functionality are better, more ergonomic and practical than anything committees come up with.


I don't see how this is the case here:

- the nested CSS proposal supports everything the SCSS syntax does, plus some potentially useful combinations

- the SCSS syntax would literally lead to a worse user experience, both for the end user whose browsing performance is now degraded, and anyone implementing parsers for CSS (since the infinite lookahead is quite a bit more complicated)

- the committee isn't just "coming up" with the new syntax, they are asking for feedback. Let's not pretend this is the same as a committee deciding something without taking input from the affected users.

I understand that generally a de facto standard will be more useful than a de jure one. But this isn't some committee coming up with their own convoluted version of a standard because of NIH - they are communicating clearly and openly around why the established standard would be problematic. Shouldn't we as a technical community try to find the best solution for the problems we face, instead of taking principled stands and ignoring the technical hurdles in our way?


It's a lose/lose situation.

If you resolve the ambiguity in real time in favor of the preprocessor syntax, you break existing websites.

If you perform a preprocessing step it now blocks CSS parsing and slows down all websites on the internet.

Maybe the preprocessor authors should have thought harder about their syntax if they wanted it to be adopted as a web standard.


This. And if you want a different solution, offer it through transpiling, and if people like it, then implement it in the browsers. Not the other way around, forcing on us a standard that noone likes.

Otherwise just use sass, it is well-liked and proven. Or, as you suggest, do nothing.


Your comment makes me feel like this is another form of "New Product helps a googler's promotion, but improving an existing product doesn't" attitude.


100% agree. The community has already chosen the syntax it wants by voting with its feet for SCSS


Except that syntax requires infinite lookahead to parse.

It's feasible when you're running a preprocessor on your own code one time, but not feasible for the browser to do on unknown code on every stylesheet it encounters.


I'm quite sure some edge cases could be forbidden to speed ip parsing. The most common use in my experience is simple nesting ("& .child"), and I would argue other forms should be avoided for readability reasons anyway.

But finding 3 other solutions (because of "performance"), not promoting them through transpiling, and then forcing them on web devs seems... googley. Not the first improvement they forced down our throats and unfortunately not the last. /rant


You can just bound the lookahead on the browser by some large number, and accept smaller performance loses for parsing. Anyway, if your possibilities are only nested selector and property, why don't you compute both until they are not ambiguous anymore? You don't need backtracking.


Jake Archibald points out [1] section [2] in that page that explains it somehow. Haven't grokked it yet, but I see that popular SASS (SCSS)-BEM way of doing

    .block {
        &__element {
            &--modifier {
                p: v;
            }
        }
    }
for getting

    .block__element--modifier { p: v; }
could really be problematic from CSS parser perspective. Also in SASS `&` really is kinda placeholder and I think you can do stuff like

    .x { &+&+& { p:v; } }
to get

    .x + .x + .x { p: v; }
what again seems problematic. (But again, I don't say I've grasped that fully.)

[1] https://twitter.com/jaffathecake/status/1552200992179077126 [2] https://developer.chrome.com/blog/help-css-nesting/#:~:text=...


I'm glad this is happening.

Languages that transpile to CSS require extra tooling and setup, and if there's one thing our web apps have too much of it's tooling and setup. I will be able to make much stronger cases to get rid of SASS, SCSS etc when this happens.


Can we not just have something like:

  a {
    &:hover {…}

    &:parent(.theme):hover {…}
  }
No crazy selectors with an ampersand in the middle allowed, but a way to apply a parent or ancestor selector, a bit like :where() or :is(). There could be :in() for ancestors.

No one needs this monstrosity:

  dialog {
    @nest html:has(&[open]) {…}
  }


I'd be happy if they just left it here. You can only nest pseudo-classes and pseudo-selectors, and everything else either use your own tool to generate the CSS or just not not nest.


Folks undervalue how much `:is()` and `:where()` can eliminate nesting and works in most browsers right now.


Honestly, if SCSS works, then I don’t see how they can say it’ll be a problem in the browser. It already works, lets not require everyone to redo everything they already have.

Have the browser show a warning in the style of “An unbounded lookahead is making this page slow” when, and if it happens, and the site owner can do something about that crazy thing.


Yes but the cost is paid once by the developer at compile time. I'm this case, the cost would be paid by every visitor on every page visit


That’s already true for all the Javascript tracking and ads. It seems silly to now worry about a few extra ms that it takes the CSS parser to resolve it’s difficulties.

Maybe if they specified how long this ‘infinite’ lookahead takes in all their scenarios, it would be more compelling.


Tracking and ads scripts aren't special here. Yes page rendering is paid by the browser every time, and this group of people is specifically worried about the CSS part of that process. They're separate concerns and don't need to influence each other design wise


Also, CSS is often used in other contexts than web browsing as well.


Really? I'm not aware of any except exports of the browser stack like Electron.


Really, it's ubiquitous. Qt and GTK use it extensively. You don't need HTML at all to use CSS, it translates well to a lot of other things.


That feels like a very user-hostile point of view. It's better to spend a bit of time to get this right rather than just doing it in a way that affects millions of web users.


Yes but that cat is so far out of the bag that it's meaningless. The browser's model is "ship source code, and we compile and run it." I would love a universe where it would be possible to ship some binary artifact to browsers so that it can load it as if it was an already opened page, complete with local state and skip all the parsing/compiling steps.


Meh - every step down that road has been fraught with significant issues. See: Flash or Silverlight, for example. (Yes, I know, not quite the same as if the browser did it natively.)

One of the beautiful things about the web is the ability to jump into the dev tools and look at the code. I know it's harder than ever to actually learn from that now, and I predict you are correct that someday we'll get to the point where it's a binary blob delivered. But I'm not holding my breath waiting for it and certainly not advocating for it. I'll adapt when/if it happens, but I feel a bit of the "magic" of the web will die that day.


Agreed.

What's the real performance cost of the lookahead on an average/decently written page?

Terrible philosophy to base the design on implementation details of the engine. Unless it can be shown that performance will degrade significantly for the average page, which I suspect not


I don’t know that I’d call this an implementation detail of the engine.

As I read this, the spec would force the implementation on every engine - and there is no known algorithm that could answer the question in constant time given the spec.

Infinite look ahead means infinite look ahead. Which means (IIUC), every time this case is encountered, any/every CSS engine (existing today or yet to be built - every CSS engine from this point forward) would have to potentially check every subsequent token until it could properly understand the context of the symbol.

Given that - I don’t like the idea of any CSS file being able to put my browser into that state.


Correct me if I'm wrong, but my interpretation of the lookahead is that performance only degrades once CSS becomes deeply nested.

That the need to lookahead only applies to syntax nested within another block.

It's not a realistic use case for somebody to nest CSS more than a few levels deep, and if the lookahead is a known part of the spec it would become a best practice not to excessively nest things.

So with side by side implementations, what's the cost in time/CPU of a realistically scoped CSS file with a realistic level of nesting? That's the answer I'd like to know.

Optimizing for an avoidable theoretical worst case shouldn't be a major consideration in design.

The net result of using a weird syntax for nesting is that developers will just keep using preprocessors forever because they're far more ergonomic. So all the spec saves is some built file size, while not improving anything about the development process


It's the classic chicken and egg problem.

The lookahead only need to be applied to nested selectors.

But you can't know whether something is a nested selector until AFTER you applied the lookahead.


> determining which should be championed through to a specification candidate

Didn’t they mean “implemented in Chrome and therefore forced into the CSS standard due to browser monoculture”?


No. This research is ahead of a CSS working group meeting, where its inclusion into the standard will be discussed.


I feel like easy nesting shouldn't be included in CSS. The only few times it's actually useful and harmless to have nesting in Sass is when we want to do stuff like styling anchor's hover and focus styles at the same time. And this is already solved in CSS with :is() and :where().

Most of the time I see nesting used, it ends up either in a stylesheet that is less readable than without it, or in generating css classes that are way more specific than necessary.


Thinking about this as someone who teaches web dev: this shouldn't happen. As others have mentioned, this goes just far-enough past the scope of CSS to just leave as a pre-processor feature. This will be an absolute nightmare for beginners and lead to a lot of messes/time wasted chasing bugs.

If you can't get the as-is SCSS syntax natively without a bunch of hacks, it shouldn't be done (a bummer, as I'd love to have that natively).

What could (should?) happen is codifying a pre-processor model into the standards track. Have a standard and then make some library the reference implementation.


I'm reminded of OCaml's `let rec` which I'm somewhat surprised to not see any mention of, neither here nor the w3c's GitHub issues (https://github.com/w3c/csswg-drafts/issues). I was originally thinking of only treating nesting from a parent to child but the child to parent relationship does complicate things (especially since it seems much more likely with CSS to need to modify a parent - assigned from a library or framework, presumably - than typical imperative or functional programming). I originally thought a decorator-type syntax where "this definition includes nested elements" would be reasonable, since any user agent or linter or whatever could be in its rights (depending on spec) to look ahead a certain distance. Obviously this wouldn't actually be too reasonable with arbitrarily distant children.

Anyway, I also initially thought I preferred the brackets since it's explicit and some of the best wording is odd to me, but I think after considering my own `let rec` idea I prefer the optional `@nest` syntax because the only odd case which is relevant - and often enough that succinct but obvious syntax is warranted - is the child->parent->* case. That's the only time I'd want a heads up while visibly parsing css.


I really don't find nesting to be a necessary part of the CSS spec and in fact find deeply nested CSS rules harder to reason about.


I like the first option, with the ampersand. Having to write @nest looks yucky, and an extra pair of curlies is confusing.


Implicit @nest every time for me .. except for example 8 where brackets (finally) made some sense. Also intrigued by the example 9 `&[open]` edge case. Wild.

Honestly tho, I think scss has it right where ampersand is only used for grouping / parenting.


What the hell. Asking people to vote with no context, hiding the only context that is provided (the "Why can't the exact nesting example shown above be the syntax for CSS nesting?").

This as an afront to both democracy (uninformed decisions only, please!) and technical decision making.

I guess someone feels like the standards body is bikeshedding and wants to flip a "democracy coin".

"Technical Trumpism" but honestly worse.


I don’t think browsers should implement this. They’d need a much more robust parser with fewer edge cases than what SCSS implement - but this then means you can’t do stuff like BEM nesting.

It’s also not necessary - you can do just fine not nesting. If you have so many selectors on one rule that the nesting is really benefitting you, it’s very likely you have bigger problems.

They should just leave it to build tools.


This should not be done on browser level, this should be done as a pre-processor, because any nested syntax is going to make older browsers faint, and to lessen the complexity of CSS, especially for beginners.

That being said, if people really want this, then it definitely should not be something where the css file is going to be filled with "@nest" definitions.

What about allowing SCSS-like syntax through something like `<link rel="stylesheet" href="styles.css" allow="nest,foo,bar" />` which then tells the parser to react in certain ways - for example if there is a rule "color:hover", then do the usual CSS parsing, while "color:hover {" means that there is a nested rule.

That way the developer can opt-in to certain features, and you could even have it in the style tag with something like `<style allow="nest,foo,bar">...</style>`

But then you have an issue of custom features allowed through the tag definition, where chrome can force their way with things even more.


Old browsers will just ignore it, just like if you give them nested SCSS without pre-processing it first. And for those that need to support older browsers, it'd probably still run through some sort of processor to output CSS for the older browsers. I see no reason to need to tell the parser what rules to parse for given how CSS works.

As far as having numerous `@nest` definitions, I don't know if your concern is file size, but if so, that really isn't an issue - assuming use of gzip (or some similar compression), there is almost zero difference in file size for repeated use of `@nest`. I did a bunch of testing related to multiple @media blocks versus a single one (a long time ago) and found it didn't make any significant difference in file size - and that adds significantly more characters in the uncompressed version than @nest does.


I have a 4th idea for syntax. Use a similar syntax to media querys, where you use the `@nested` query with parentheses. Maybe too verbose?

.has-nesting {

  @nested (> .nested) {
    /* rules */
  }

}


Looks like another round of W3C bike-shedding an existing easy-to-use and well-understood community standard until it's godawful to work with and half as useful.

Gotta love standards bodies!


[flagged]


No, these are the kind of people who are deliberating on the best syntax to support millions of projects using CSS for the next 30 years. They have to factor in backwards compatibility, extensibility, performance, security, accessibility, responsiveness, and lots of other NFRs.

Once it’s out there, it has to be supported for a long time. It’s worth deliberating to get right. Nobody will die because they took their time.


Kinda? People have been working with their preferred syntax for oh, the last 10 years or so?

It’s scss.

No point in making a poll with an answer that’s already decided.


They mention in the post this has issues because browsers don’t have a build step in the same way Sass has. What’s your solution for that?


Support look ahead to some limit, then fail that css resource, with an error message that is more verbose in the dev console.


One of the goals for the web in general is to not impose the burden for that kind of strictness onto users. It's why we reversed course away from XHTML, which would refuse to render a page if the markup was incorrect.

Put another way: inconveniencing developers is preferable to inconveniencing users.


I like this idea. Could even bake a warning into VSCode.


Sounds like a good idea. Are you able to suggest it to them?


scss avoids a lot of problems because it's a preprocessor


This is just css. No one's going to die.


So now we discuss web standards on chrome.com ? This looks like the beginning of the end of a free web governed standards established by an independent body. I would have expected such an article to be published on w3.org...


Things don't get a lot of eyeballs on them on w3.org which defeats the object of the post which is to get developer feedback. It's pretty normal for CSSWG members to ask for opinions via tweets, on their own blogs, or in other places. I've posted stuff to my personal blog in the past. This is something that will be worked on in the CSS Working Group, it happens to have people from Chrome working on it, however it will be discussed with the rest of the CSSWG who have a range of affiliations: https://www.w3.org/Style/CSS/members.en.php3


The request for feedback could have been discussed at w3, and google could still have made a blog post about it saying something like "this is what we have been discussing at w3 so far, your feedback is welcome on w3.org"...

The post is on chrome.com and mentions the decision makers as "we".


nesting will be nice when it lands, but i'm most excited by far for :has() support. i know chrome and safari have initial implementations in place and firefox just recently started. :has() will be the biggest advancement in css since grid, moreso than nesting or container queries, because of the power it puts in the stylists' hands. unfortunately trying to track its progress is a haphazard gander into an opaque maze of different sites and bug trackers.


It also looks like asking the general public for a go to make CSS even more complicated, when we all know it's a one-way ticket to Chrome dominance, ie a rhetoric dictators have been using. Note the absence of the option to "do nothing" and keep good-damn CSS as it is already after 25+ years of tinkering that haven't solved its problems.

I mean even one of the long-term editors of the CSS spec has raised hands and warned against including ever more inessential and author-oriented features [1]. That was almost 14 years ago.

[1]: https://www.w3.org/People/Bos/CSS-variables


CSS has evolved tremendously. Variables are amazing, so is flex and grid.

Nesting is way overdue. But of course we shouldn’t be discussing it at Google’s site.


Variables (since they are not just constants), flex and grid all allow doing things that were previously not possible or at least not reasonably. Nesting is a pure syntax change that is already solved via a preprocessing step. So yes, we should weight what this adds in convenience vs. the cost of the adding this to all browsers, maintaining support for it forwever, the impact it has on future additions as well as new websites using this not working on older browsers. Note the should - I'm perfectly aware that the web dev ecosystem already often ignores many of these.


We kind of had variables with preprocessing before. Or a simple sed ‘s/str/replace’.

But true support is convenient. One of the best places to write CSS is the browser’s dev tools. Having CSS variables work there is awesome.

If it can be parsed almost as fast, I say go for it.


Those are constants. CSS variables are more powerful than that since they can be changed at runtime which allows e.g. minesweeper in pure HTML and CSS: https://github.com/propjockey/css-sweeper


Scoping and nesting are two things that would solve 99% of problems when it comes to organising CSS (and components). They should definitely be on the browser level and not "solved by preprocessors".


I disagree. Given the enormous complexity browsers have aggregated already, we should strive to make browsers "player apps" (like MP3 players): a relatively simple front end with huge versatility in server apps and back ends, rather than, like, everything for everybody. I'm aware that ship has sailed a long time ago, but piling ever more not-so-well-motivated stuff onto browsers just means the next generation has to start over with a clean slate; actually I think this is inevitable already.

CSS nesting is clearly serving the use case of components in web apps, as opposed to mere content-driven web sites. IMO a sensible approach would be to give web app developers powerful ways to tie into rendering and layout programmatically (a la Houdini API) when those apps will require JavaScript, and in many cases use CSS-in-JS anyway, rather than burden browser cores with ever more questionable CSS features.

"Would solve 99% of use cases" also sounds like famous last words ;)


> a relatively simple front end with huge versatility in server apps and back ends

Define "relatively simple"?

Nesting and scoping in CSS don't make frontend any more complex. CSS has had problems with it's flat top-level structure since the very beginning of its existence. Approaches like BEM didn't appear out of thin air.

> CSS nesting is clearly serving the use case of components in web apps, as opposed to mere content-driven web sites.

Even a content-driven web site has a plethora of repeating and/or complex elements that are a pain to define and maintain.

> Would solve 99% of use cases" also sounds like famous last words

Google has poured hundreds of millions of dollars into web components whose primary use case (that is, what they are being used for in reality) could've been solved by scoping and nesting.

Same goes for all the CSS-in-JS abominations.


AFAIK, this is just syntactic sugar. A shortcut.

It’s not scoping in the inheritance sense. The C in CSS is for cascading. Inheriting is the natural way to be for CSS


Casacading !== inheritance. These are very different things. I wish we had inheritance (or traits) in CSS.


Well tell me the difference then. We have cascading inheritance, that’s what CSS is all about.

Unless we’re using different definitions of the term


Casacading alpplies to all elements in the cascade. Whether you want it or not.

At the same time it's impossible to say "hey, I want to inherit from that particular style", and not have the cascade apply to it. For example, I want a button and I don't want anything in the hierarchy above overriding its borders, or colors, or fonts, or...


Oh, I see. Arbitrary non hierarchical opt-in inheritance. Or perhaps a cascading inheritance opt-out, and you could add classes back as you please. That would be cool.

Not sure how it would impact browser parsing and drawing, but it sure would be useful.


I agree it shouldn't be on chrome's website, but nesting in native CSS is a very welcome and needed change, and one of the last things I consistently reach for SCSS for. It's been requested for years.

It does have the ability to be a foot-shotgun, but so do 3/4s of the features in CSS if you don't understand them. And people that want it would probably just do the same thing in SCSS anyways.


> asking the general public ... when we all know it's a one-way ticket ... a rhetoric dictators have been using. Note the absence of the option to "do nothing"

regrettably reminiscent of the Wikimedia Foundation


> That was almost 14 years ago.

Now that's a page that can be dated by its look.


>The distribution is as you might expect, lots of very small files, very few large files:

> - Half the files is less then 7 lines. > - 90% is less than 163 lines. > - Only 0.6% is longer than 500 lines.

This part jumped out at me. Those were the days...


"it hasn't been done before, so it can't be done now". 25 years ago isn't 14 years ago isn't now...


Nobody's saying it can't be done. They're simply questioning whether it should be done, since we have tools that solve this problem already. It's not giving us anything we don't already have, and so the value proposition in making the syntax more complex is pretty shaky.


Why should the tool be necessary? I'd much rather write plain CSS than use Sass, and nesting makes that significantly more readable and easier to maintain.


Whether the tool should or should not be necessary is an entirely different question. The reason it is necessary is because it enabled writing "CSS" in a way that it was never meant to be written. And as evidenced by the blog post and all of the discussion here, adding the functionality to browsers isn't at all straightforward.

So a better question is why shouldn't the tool be necessary? It's all about writing the same CSS in a more developer-friendly way, not enabling CSS to do something it never could before (like variables and flex did). Why should browsers have to worry about this added complexity? Just write your stylesheets however you'd like, and then run a tool to compile them down to standard CSS.


When I look at these proposals, I would still prefer using Sass over regular CSS. It's syntax is just cleaner.


I too am salty about this, but the W3C uses IRC for discussions and have no easy forum or boards for RFCs (like TC39 with their use of GitHub for ECMAScript proposals).

Contributing to web standards is not really accessible and that contributes to this Chromium-centric world.


The CSS Working Group uses GitHub primarily, and any minutes from IRC are added to the issue. Here: https://github.com/w3c/csswg-drafts/issues

You are very welcome to contribute!


What kind of chat would be more accessible than IRC?


What kind of chat wouldn't be more accessible than IRC?


> Contributing to web standards is not really accessible

How so? AFAICT if you want to contribute just go contribute. It's open to all. Discussions are had on public mailing lists, github, etc...

Here's one place to start

https://github.com/w3c


Where would one raise a proposal for something like adding a `sandbox` flag to `<script>` elements, the proposal of adding a feature like a "Context Menu API" for PWAs or discussions around pro-privacy features?


Hey, I am just happy that survey is not on Google Forms requiring a G account.


Afaik browser devs are dictating the standard for many years already.

Also, the post ackshually refers to a W3C draft at https://www.w3.org/TR/css-nesting-1/


Why didn't they publish one then?


> This looks like the beginning of the end of a free web governed standards established by an independent body.

The end sailed us by a few years ago.

Chrome now dominates all standards bodies and pushes quite a few of its proposals forwards with utter disregard for anyone.


Any person can publish a blog post discussing web standards on the “free web”. You’re welcome to publish one yourself.


Too many Google engineers working on this to keep up.


It seems like the ideal approach would be "proving" syntax proposals by implementing them in a preprocessor first and then later trying to standardize the syntax if it gains traction. It sounds like there are technical reasons for why that didn't work for the case of SCSS-like nesting, which is disappointing but understandable... if that's the case I think the right approach is to take these new proposals and "go back to square one" (preprocessor) vs. "go straight to standardizing something new".


Just make it like scss. It’s what most people that use nesting in css already know and has been shown to work


There's "@nest" and "optional @nest". For a fair comparison, shouldn't there also be "optional brackets" next to "brackets"?


I was hoping the new CSS syntax for nesting would work just like in Sass, simple and intuitive. Unfortunately, it's apparently too ambiguous and inefficient for parsing.

> Nesting style rules naively inside of other style rules is, unfortunately, ambiguous—the syntax of a selector overlaps with the syntax of a declaration..

> It is, in most cases, eventually possible to tell properties and selectors apart, but doing so requires unbounded lookahead in the parser; that is, the parser might have to hold onto an unknown amount of content before it can tell which way it’s supposed to be interpreting it.

> CSS to date requires only a small, known amount of lookahead in its parsing, which allows for more efficient parsing algorithms, so unbounded lookahead is generally considered unacceptable among browser implementations of CSS.

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


>> “official CSS version of this syntax is being strongly considered and we have a split in preference that we'd like to employ the help of the community to break the tie.”

Where? Is there reason Google moderating this? Are there options Google has not suggested?


To be honest, I just don't care anymore about plain CSS, it is just an intermediate representation to me. This feels like bikeshedding over the syntax for LLVM IR.

Just add SCSS to the web inspector that's the only place where I'm still exposed to plain CSS.


Wildly different experience in that I'm back to writing plain CSS on many simple to medium complexity projects because these tools aren't providing me enough value for the complexity and dependencies they bring. I've not written Sass or SCSS in about 4 years now, and when I do bring a tool, it's a countable number of PostCSS modules to bridge some gaps for features that haven't yet been upstreamed to all browsers.


I vote for #4 - use SCSS syntax with bounded forward-lookahead.

> CSS to date requires only a small, known amount of lookahead in its parsing, which allows for more efficient parsing algorithms, so unbounded lookahead is generally considered unacceptable among browser implementations of CSS.

Then choose a bound! Make it big enough that it'll cover 99.9% of use cases, ignore any CSS rules beyond the bound with a console warning, and go on with life...

This seems so obvious that I feel I must be overlooking something. Perhaps the bound would be so big, that when parsing multiple stylesheets in parallel the memory use would be unacceptable?


If that `color:hover` issue is the problem, then we should have pseudo classes like that change their syntax by using a double colon:

    color::hover { ... }
Or maybe class names can't be protected CSS properties. So `<div class="color">` would need to become invalid.

Protected names are commonplace in programming. And I'd argue it leads to better software development by causing less confusion.

If they make it too verbose I'll just stick with SCSS. Honestly, I love vanilla CSS, but nesting is a huge need; it's the only reason I use SCSS to begin with.


I guess I'm old and set in my ways, but I just don't want any of this.

I never missed nesting.

Don't Shadow DOM and components kind of make this not very important?

I've mainly seen nesting used in huge multi-kloc scss files that applied to the whole site, which I find a bad idea anyway.

I guess what I mean is: for small pieces of CSS it doesn't matter, and for huge nested structures.... you shouldn't do it


Nest definitely best.

But instead of ampersand, let's use forward slash. It's very webby.

Now for this very fascinating article on the history of the ampersand

https://www.merriam-webster.com/words-at-play/the-history-of...


Slash has a long history of being used as a tree descent combinator. That would make sense for the descendant combinator (“ ”) or the child combinator (“>”), and XPath does basically that (approximately “//” and “/” for descendant and child); but to use it instead of the ampersand here, that would be silly, because the ampersand is representing a selector, not a combinator.

Ampersand has a long history, though much less popular, of being used to represent the source value of a text replacement (e.g. in Vim, :s/search/<&>/ will find “search” and replace it with “<search>”). This is pretty much exactly what’s going on here.


I don't think it's silly. Is it really silly tho? I think it might be silly if you want to make everything historically align tho. Not everything has to.

You might shoehorn yourself into an unideal outcome, trying to be backwards compat with the past. No?

I like the conceptual similarity of stepping through parts (like a URL) with stepping through parts of the selector.

Does it look good?

  form {
    margin: 1rem 0.5rem;
    background: transparent;

    / fieldset {
      border: none;
      padding: 0;
      background: white;
    }

    / label {
      color: darkslategray;

      / > input {
        border: thin solid;
      }

      / input {
        border: none;
      }

      / > button {
        font-size: larger;
      }
    }

    / a {
      text-decoration: none;
    }

    / :is(a, input, button, label) {
      color: inherit;
    }

    /:invalid {
      color: red;
    }
  }
I think it looks really good actually. And I think the humble slash works really well as a stand in for a selector here.

&&& are fucking noisy.


Although honestly it does look a little bit like a comment.

Maybe & are the way to go.

But...what about question mark? Question mark could be good. Or underscore.

  form {
    margin: 1rem 0.5rem;
    background: transparent;

    ? fieldset {
      border: none;
      padding: 0;
      background: white;
    }

    ? label {
      color: darkslategray;

      ? > input {
        border: thin solid;
      }

      ? input {
        border: none;
      }

      ? > button {
        font-size: larger;
      }
    }

    ? a {
      text-decoration: none;
    }

    ? :is(a, input, button, label) {
      color: inherit;
    }

    ?:invalid {
      color: red;
    }
  }
I think question mark's pretty good. It's the most semantic, visually for me. Maybe...I think.

What about underscore?

  form {
    margin: 1rem 0.5rem;
    background: transparent;

    _ fieldset {
      border: none;
      padding: 0;
      background: white;
    }

    _ label {
      color: darkslategray;

      _ > input {
        border: thin solid;
      }

      _ input {
        border: none;
      }

      _ > button {
        font-size: larger;
      }
    }

    _ a {
      text-decoration: none;
    }

    _ :is(a, input, button, label) {
      color: inherit;
    }

    _:invalid {
      color: red;
    }
  }
Looks weird to me. But some people might like it.


What I care about would be a SCSS to CSS nesting migration tool. Has anybody started work on that? 95% of my scss usage is simply nesting, variables, and color mix functions. Which means I could pretty much switch to a native solution when available.

I don't really care which is chosen, I can learn to live with whatever.


I hate it. I hate it all.

I voted for `@nest all` because I don't think it's a feature I will EVER use, so I wanted it to be dumb obvious that someone is using it by explicitly putting @nest in the code. That way if I encounter it in the wild, I'll be less likely to confuse it with SCSS nesting, for example.


Has Google finally learned something from the alert change debacle and is asking for developer feedback first?


    a { ... }      a { ... }
    a b { ... }    a > b { ... }
  is to          is to
    a {            a > {
      b { ... }      b { ... }
    }              }
That's what I want. And it appears to fail correctly.

The lookahead argument doesn't warrant poor syntax.


Yikes. I guess I would have to go with `&`, it isn't that awful.

But we'll all still probably keep on using SCSS. So maybe something like `@nest` would be better to differentiate both, and the compiled result would still be a bit smaller.


Yeah I don't see whats wrong with the current css solution? Drilling down tags (the current implementation) just makes it more explicit in my opinion.

I feel like nesting is going to take nice flat easy-to-read css and make it too complex and deep.


I wish there was fourth option for “brackets but optional”. I like the brackets syntax, it’d be nice to omit them if you only have rules starting with &, similar to the “directive but optional” syntax.


I learned a long time ago that this kind of approach only brings problems in the long run: you end up building house of cards all the time. All our problems went away when we adopted functional CSS.


None of these seems meaningfully different enough for me to care. I could see myself getting used to any of them. Will be very happy when this goes through.


I think both looks like something that I can live with. Perhaps @nest would be preferable. Why call it @nest though? @this or @scope or something maybe...


Oh my, I much prefer @nest only even though it's verbose. The bracket method has good intentions, but a readability nightmare for me.


I'm in favor with option 1 (@nest, &).

Reasoning: I do not see much sense in the use of additional brackets (option 3), since "&" already serves as a separator. I also do not see much sense in the use of the more verbose "@nest" (option 2), since conventional selector syntax uses single-character keywords already and there is no real parallel to things like "@media" or "@page" (as this isn't related to rendering and formats).


I support @nest always with implicit &, so

  @nest .child {}
equals

  @nest & .child {}


How about:

  x = .nesting {
    color: hotpink;
  }

  y = x > .is {
    color: rebeccapurple;
  }

  y > .awesome {
    color: deeppink;
  }
plus:

  x = .nesting;
  y = x > .is;
  z = y > .awesome;

  x {
    color: hotpink;
  }

  y {
    color: rebeccapurple;
  }

  z {
    color: deeppink;
  }

The names x, y and z aren't ambiguous because they are lexically defined. They could be allowed to shadow tags:

  pre = .foo > .bar;
then from that point on, pre isn't the HTML tag, but the above definition. Or else tags could be reserved: cannot use those as names. (Causing trouble when new tags get introduced; bad idea.)

Regarding the @nest syntax, why couldn't it look like an attribute?

  ...sel... {
     color: blue;
     nest: > .whatever { ... }
  }
Or just the colon:

  ...sel... {
     color: blue;
     : > .whatever { ... }
  }
Why use containment to express the nesting? What is being nested is the selectors. The braces group something else: the attributes affected by selectors. Why would nested selectors have to go into those braces?

The + character at the start of a rule could mean "the selector of the previous rule":

  .nesting { color: hotpink }
  + > .is { color: rebeccapurple }
  + > .awesome { color: deeppink }
Or use a master outer brace for this:

  {
      .nesting { color: hotpink }
      > .is { color: rebeccapurple }
      > .awesome { color: deeppink }
  }
The understanding is that the rules grouped in the brace have a cumulative selector, rather than independent selectors.

Or what if braces just group, and @nest (or whatever) is required to indicate the selector stacking semantics:

  @stack {
     .nesting { color: hotpink; }
     > .is { color: rebeccapurple; }
     > .awesome { color: deeppink; }
  }
Now you can have N levels of selector refinement without N levels of syntactic containment. The containment is still available:

  @stack {
     .nesting { color: hotpink; }
     > .is { color: rebeccapurple; }
     {  
       /* parallel rules: not a stack */
       .awesome { color: deeppink; }
       .terrific { color: yellow; }
     }
     @stack {
       /* stack-in-stack, cross-producting. */
       .even { color: red; }
       .more { color: green; }
       .nested { color: blue; }
     }
  }
Here, because the nested @stack follows a block of parallel rules, a Cartesian product semantics can come into effect. That is to say, it is equivalent to:

  @stack {
     .nesting { color: hotpink; }
     > .is { color: rebeccapurple; }
     {  
       @stack {
         .awesome { color: deeppink; }
         .even { color: red; }
         .more { color: green; }
         .nested { color: blue; }
       }
       @stack {
         .terrific { color: yellow; }
         .even { color: red; }
         .more { color: green; }
         .nested { color: blue; }
       }
     }
   }
 
The @stack following a parallel rule causes the @stack to distribute into the parallel branches.

Kind of like Bash brace expansion:

  $ echo nesting-is-{awesome,terrific}-even-more-nested
  nesting-is-awesome-even-more-nested nesting-is-terrific-even-more-nested


I liked the colon as well but the problem is

  i {
    font-variant: italic;

    ::hover {
     color: pink;
    }
  }
Is that ::hover or : :hover? As in

  i::hover 
Or

  i :hover
I think strictly speaking it is clear (it's the first one, as : is mandatory), but it doesn't look clear.

Like

  input {
    border: 3000px groove purple;
    color: transparent;

    :::placeholder {
      color: revert;
    }
  }


The mandatory : could be followed by required whitespace, making :: a syntax error.

Or the whitespace could be strongly recommended, with implementations encouraged to diagnose if it is missing, at least in ambiguous-looking situations like ::hover. (Is that a forgotten space? Or a forgotten colon?)

You have a space in:

    border: 3000px groove purple;
    color: transparent;
So just for consistency, you want it here:

    /*empty prop name*/: ::placeholder {
      color: revert;
    }


Ah this is the problem I originally anticipated with : when I first came up with the idea too. You see the problem is then (if you force a space):

  div {
    color: blue;
    : :hover {
       color: red;
    }
  } /* (1) */
Is unable to represent

  div {
    color: blue;

    /:hover {
      color: purple;
    }
  } /* (2) */
(or if it (1) means (2) then it is unable to represent the A below)

In other words you can write

  <div>Hi<span>there</span></div> 
And using the first one get a red there on hover (A). But the second one intends to get a purple Hi there on hover (B). But You can't express that because the mandatory space clashes with the CSS (implicit) descendent combinator (space).


Why not both? - Zoidberg


The @stack idea together with the named selectors would be pretty comprehensive.


It's blindingly obvious that SCSS syntax for this should be used.


I prefer the way & .class looks, its just like LESS


How about no?

Why do we need to add further complexity into the already complex thing that are browsers?

As the post mentions at the beginning, this can already be done with pre-processing. What do we gain by having more syntax sugar?


The ability to eliminate the need for pre-processors.


I see, chrome developers are, as usual, very busy people. No time to waste - CSS won't break itself! (@nest {sarcasm: red;} )


*backets -> curly braces


Is it not obviously?


Use sexpressions?


can we just get native styl support into the browser please


One thing CSS still can't do is to properly put multiple elements in a single flex or grid without having to wrap it with a div. I'm glad to see work being done on CSS but I somehow seem to frequently run into things CSS still can't do and there is always some proposal that isn't supported in both Chrome and Firefox.


If verbosity is the issue we're trying to solve for then it seems obvious that the answer is to simply use the nest emoji instead of "@nest".

https://emojipedia.org/nest-with-eggs/


Don't try to imagine yourself writing this code. You won't have to. We have preprocessors and they'll always be better than writing plain css. One of these standards will be what preprocessors of the future will need to write. The result will be that your compiled css will be smaller than what is possible with today's standard. So don't choose the option that's easiest for you to write, choose the one that's small.


I don't think what you describe is actually the goal of this proposal.

I don't think anyone involved in the standards-making is looking at how many bytes of CSS would be saved -- I don't think the number is likely to be significant to anything, and I don't think this is the motivation.

My understanding is that the intention is really to make CSS pre-processing less necessary, take features that people have found the need to have preprocessors for, and put them in the standard so the preprocessors aren't necessary for those features.

I feel like I've seen some of the undesirability of preprocessors in my experience with sass/scss, which is having to make backwards incompatible changes in order to avoid conflicts with the evolution of CSS underneath it. Also just having to setup the pre-processor can actually be a significant infrastructure burden -- Rails had to kind of completely change how it supported SCSS when SASS moved to supporting dart-sass as essentially it's only supported implemnetation (no more libsass or ruby-sass). And then what happens when one of your dependencies uses scss and another uses postcss. I understand the desire to get back to not using pre-processors.

You can definitely have your own goals and motivations and encourage people participating in this user poll to adopt them too. But if they aren't the goals the committee is working towards, they're unlikely to be achieved very well... and there are probably reasons they aren't.


I believe your understanding of the intention is correct and I totally agree with what you're saying about those involved in creating the standard. I just don't think they'll be able to evolve CSS into something preferable to using a preprocessor for many many years if ever.


Totally disagree with this sentiment (that CSS will always need to be pre-processed). In many cases today it isn't necessary, even for fairly complex projects. This (nesting) is one of the last issues that causes me to reach for a pre-processor, honestly.

So (IMO) we should absolutely be looking for what makes the most sense for human devs to write and read, and that should be the default for the base language that the browsers can interpret. We can always make it smaller via minification, etc.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: