-
Notifications
You must be signed in to change notification settings - Fork 676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[css-nesting] Syntax suggestion #4748
Comments
The drawback is that it adds an additional level of indentation. The mandatory |
So the proposal is that a bare I wouldn't be opposed to that. It would save quite a few characters in nested rules with high breadth and low depth, and would make them easier to read too without the repetitive Do note however that @tabatkins has objected to defaulting to descendant selectors when no |
I'll be completely honest here—I don't fully understand everything going on here. ¯\_(ツ)_/¯ I build with CSS; I don't build CSS. ;) So all I can say is that I have a strong gut feeling that implementing native CSS nesting in a way that breaks the compatibility that SCSS currently has with pure CSS is a bad move. Yes, I know the onus is not on CSS to retain that compatibility. Regardless, I feel like intentionally breaking it (or allowing it to break) when there are other ways to implement things will actively disrupt a large portion of the web. I presume that for whatever reason, implementing native CSS nesting in the exact same way that SCSS already does it has been ruled out? |
The Nesting spec goes into detail about why that's not an option. |
@proimage Current spec is close to perfect, and it doesn't break compatibility with SCSS. SCSS couldn't be used in browser directly ever, you always had to use the SCSS preprocessor. So, just continue to use this preprocessor, and everything will be fine. It is not mandatory to use native CSS nesting when it will be available in browsers. But I wold use it, because it seems that it is designed a bit better than the SCSS nesting. |
Great! If that's the case, then I have no objection. :) |
If authors of CSS preprocessors want to allow mixing of the native CSS nesting and SCSS-like nesting in one file, they could rely on |
My main issue with the specified approach is that there are two distinct syntax rules for authors, in order to solve a browser-parsing issue. While I understand the parser requirement in play, I don't like passing that along as an inconsistent syntax, where authors have to understand the parsing distinction. It would be great if we could move towards a more consistent single syntax, no matter what nested selector you plan to write. In talking with @fantasai, we had a few ideas for a variation on the approach suggested above: div {
background: black;
color: white;
/* curly braces always fence off nested selectors, so we avoid syntax disparities */
/* by default & is required in all selectors, to establish relationship */
{
& span { /* div span */ }
p & { /* p div */ }
}
/* multiple nesting blocks are allowed */
/* and are able to prepend a combinator for the entire block */
/* (or & for descendant) */
& {
span { /* div span */ }
p & { /* div p div */ }
}
~ {
span { /* div ~ span */ }
p + & { /* div ~ p + div */ }
}
/* could also use & before combinator, for the same results */
& ~ {
span { /* div ~ span */ }
p + & { /* div ~ p + div */ }
}
}
|
Hmm, interesting idea. Similar (and simpler, even) than the suggested So what would multiple-level nesting look like?
Or, in the inline-braces style y'all seem to prefer for some reason... 😉
All I'll say is, my eyes certainly aren't used to visually parsing that code... granted, color-coding would help a ton, but still... 😆 |
I agree. I know lots of people have worked on this, but I've been following the discussion and I also feel like passing down the burden of having 2 syntaxes to solve a parsing problem to the CSS author is not the best move. If @nest is able to forgo the requirement of the selector having to start with &, it just means it's a parsing issue that can be solved for the "regular" syntax too. And it's much cleaner to use and read when blocks are nested naturally with curly braces. I'd say the last example by @proimage would be repetitive though, I would wish the curly braces to be unique per level if it was possible. Right now, CSS parses everything inside braces as a style declaration and that's the difficulty "@nest" and "starting with &" are trying to bypass : it's easier to parse the start of a nesting selector if it starts with something precise. But really if there's another curly braces block inside, it should be reverse parsed... or the rule could be "what precedes a curly brace needs to be a selector" point bar. Again, same as some others chiming in: I have not worked on the parsers, I don't know the real difficulties of this, but if CSS parsers need to be rewritten to allow this, I would way prefer to wait and do it properly than ship this as it is. Here's an example of the syntax I'm trying to explain: .main-nav {
display: flex;
ul {
list-style-type: none;
li {
margin: 0;
padding: 0;
}
a {
display: flex;
padding: 0.5em 1em;
}
}
nav& {
display: block;
}
} Becomes .main-nav { display: flex; }
.main-nav ul { list-style-type: none; }
.main-nav ul li { margin: 0; padding: 0; }
.main-nav ul a { display: flex; padding: 0.5em 1em; }
nav.main-nav { display:block; } |
@davidwebca Sadly, it's just not possible to "reverse parse" anything in CSS. For performance reasons, browser engines need to distinguish between a property and a nested selector with only a single token. |
As was posted when I raised the very same issue, the answer is given here (in the expandable green details+summary box): https://drafts.csswg.org/css-nesting/#nesting You're not wrong in that it would be an ideal syntax from a code authoring perspective, but from the perspective of the parser, it would exact too high a performance toll. EDIT: Dangit, comment-sniped! ;) |
I love this idea. I agree having two syntaxes depending on the location of the |
So, put simply, if, within rule braces, the parser encounters another opening brace, combinator, or ampersand (which isn't part of the value for a declaration), it should then go into selector parsing mode, instead of declaration parsing? Interesting. I guess any preceding declaration values need to be capped off with a semicolon, to avoid a comma as a combinator being confused with being part of a value. |
I think what we're talking about here is dropping the ampersand entirely (unless the parent selector needs to be injected in the nested selector in some other position) in favor of curly braces wrapping any nested selectors. Keeps the syntax simpler—no need for both |
Would this open the door for selector concatenation via the
|
Dropping & removes the ability to combine and reverse order of selectors so I wouldn't drop it. I think what we're suggesting here is to merely drop the requirement of the selector having to start with & (or to use @nest to allow it). Right now, the draft spec requires the selectors to start with & to pass the nesting requirement. Here's an example that is invalid: .card {
display: flex;
.image& {
display: block;
}
} To make it valid, you need to prefix it with "@nest" like so: .card {
display: flex;
@nest .image& {
display: block;
}
} So what we're saying, in short, is to drop the @nest requirement and stop dancing around the current parsers limitation to allow authors to have a cleaner syntax without those two versions that could end up being confusing and allow the first example to be valid (if it works with @nest, why wouldn't it be able to work without?) Sidenote, I personally don't care about selector concatenation. That, I can understand it adds a level of complexity that jumps into many more parsing hoops and issues and I don't think people absolutely really need it. BEM authors can use BEM with combinations instead of concatenations (.card__image would be .card.__image) and still be readable. If it was possible without being too complex and without adding too much parsing time to browsers, I would like it, but maybe we're not there yet. |
I'm really not a fan of the additional set of braces; it adds two indents for each level of nesting. I'm not sure what your additional three syntaxes are doing - is a bare selector allowed (not nested in an extra {}) if it starts with an Is the Actually, hm, it looks like you still can't put properties directly in the block; the nested block must contain style rules, not declaration lists, right? Then yeah, I'm still on the "two indents per nesting level sounds bad" train. Finally, the current rules allow us to avoid having to write a heuristic for determining when a selector is meant to implicitly chain from the parent and when it's explicitly referencing the parent instead. These appear to bring that heuristic back, so we have to decide, for example, whether a |
The double curly braces from @proimage were not necessary as detailed in my last comment. Is that what you're referring to? I think the idea here is to mainly combine the regular syntax in the proposal with the @nest rule so that a nested selector is not obligated to start with & and avoid having 2 different nested selector syntaxes. 🤔 |
I'm not the biggest fan of it either, but I think it's preferable over the current That said, what if we used some other separator character or series of characters to define a nesting block—one that doesn't have an implication of indentation? I can't think of any single char that would fit the bill, but what about something like this?
|
I don't think it's hard to remove any heuristics from our proposal, and require explicit There would be another approach to achieving single-syntax, which is just to require div {
prop: value;
@ & em { … }
@ main & { … }
@ & ~ div, p + & { … }
} Though double-nesting still feels the easiest to read and write in my opinion. |
I agree. The double syntax bothers me more than anything else that we've been discussing. Whatever the decision in the end, I'd rather have a single syntax with more indentation OR @nest so that avoids all confusion when reading CSS code at a glance. Then, I don't mind more indentation and I don't hate the "@" suggestion from @mirisuzanne above. My only gripe with @ is that it's already used to start special keywords like @media and @Keyframes. What about ">"? It must have been suggested before, has it? Or would it be too confusing with the direct child combinator? div {
color: blue;
> table td & {
color: orange;
}
} div {
color: blue;
? table td & {
color: orange;
}
} div {
color: blue;
- table td & {
color: orange;
}
} |
ASCII soup isn't great if we can avoid it; anything we choose here becomes probably unusable in Selectors in the future, too. If the group thought it was really worthwhile to have only a single form, using only |
If we're wanting to annoy preprocessor users as little as possible, then I think the "wrap nested selectors in a block" approach would be easier to transition to than the "append If we go down that route, it would be great if the chars used to define said block were one of the ones already considered "containing chars" by code editors... i.e., the chars or char pairs that can easily be placed around a selection: To be honest, I'm still trying to figure out if a JSON-esque approach makes any sense:
|
Mmm that's an interesting idea that I didn't consider honestly. That's one thought I was bouncing around in my head when we were exchanging in the previous few comments: "@" rules usually have a set of specific rules ( In that sense, the nested "style" declaration from @proimage makes syntactical sense. Plus, usually, at-rules only use parenthesis when there's a need to use special characters such as colons inside the arguments. Ex.: So an interesting idea that would be easy on existing parsers would look like: .card {
display: block;
(article&) {
display: flex;
}
} would yield .card { display: block; }
article.card { display: flex; } But this conversation is running in circles because we're trying to solve three friction points at once when it might not be possible, unless we speak with people who code and optimize those parsers:
Also I want to clarify something about my previous comments on "reverse parsing". I didn't mean "let the parser do it's thing, parse and reverse engineer the selectors afterward". What I meant was to start parsing by the deepest level of nested blocks because it's easy to infer that the previous "rule" is a selector (what precedes "{" is always a selector or an "@" rule). It might be what they already do internally and I wouldn't know about it 🤷 but in that case, it would mean we don't need a starting character at all. Note: Arguments made by @tabatkins here are still very much valid, but I'd love to hear them in a renewed way one year later. |
Just brainstorming here: What if instead of the curly braces, we prepend the list of nested rules with something? E.g. Alternate idea: What if only the first rule needs to start with |
@tabatkins That seems like a really good trade-off for not writing I don't understand the resistance to another level of indentation. If your indents are too long, stop using 4 spaces. If you're not mixing in declarations you can also just double up your braces and indent one level.
@LeaVerou I don't think I like that kind of statefulness, where what's parsed before as a sibling construct affects so fundamentally what's parsed after. |
Where was this poll even posted. Rip, completely missed it... I'm not sure it reached the audience 😬 |
It was posted here: https://developer.chrome.com/blog/help-css-nesting/ |
i prefer |
I prefer the combination of @nest and ampersand. To me it feels like the easiest transition from using SCSS. I don’t mind using |
@chee If I understood everything correctly, all you need to know about So there is not much to learn. Whereas TL;DR:
|
Since this is on the agenda for today and I can't join, I'll post my thoughts here: While the current spec is not ideal (authors basically want Sass-style nesting), it's far, far better than any of the other solutions posted here. I have been using the current syntax very extensively through PostCSS and it works well. I very rarely need to use |
I concur, but I think it would be nice to have this close with a summary of the agenda discussion, so I won't close it quite yet. Thank you, all, for a very thorough, 2.5 year (😲) look at this issue! |
I agree, in practice the spec syntax works best, despite the apparent complexities. But at this point we actually need to revert the previous resolution - where we voted to use brackets instead. As one of the people behind that resolution, I'm now fully in support of reverting it, and implementing the spec as originally written. |
Yikes, yes. How did that happen? |
The CSS Working Group just discussed
The full IRC log of that discussion<TabAtkins> Topic: Nesting syntax<TabAtkins> github: https://github.com//issues/4748 <fantasai> ScribeNick: fantasai <fantasai> TabAtkins: Some time ago we had a discussion about what style of nesting syntax should use <fantasai> TabAtkins: major options are what I drafted, nesting selector directly with an & <fantasai> TabAtkins: or use @nest if needed <fantasai> TabAtkins: option 2 is @nest always <fantasai> TabAtkins: and option 3 is using brackets to wrap nested rules, never use @nest <fantasai> TabAtkins: At the time we did a straw poll, and WG resolved on using brackets <fantasai> TabAtkins: I was unhappy with this and we resolved to take it back for more data collection or arguments <fantasai> TabAtkins: Adam Argyle ran a poll with significant response numbers about this <TabAtkins> https://developer.chrome.com/blog/help-css-nesting/ <TabAtkins> https://github.com//issues/4748#issuecomment-1211280306 <fantasai> TabAtkins: Linked article, I think was written fairly <fantasai> TabAtkins: It seems responses are incredibly one-sided <fantasai> TabAtkins: Using & or @nest directly <fantasai> TabAtkins: This is what's in the current spec and what I prefer <fantasai> TabAtkins: This was an overwhelming response in favor of one option <fantasai> TabAtkins: So suggestion is to revert previous resolution and keep syntax in current spec <astearns> option 1 also allows you to write as if @nest is always required <Rossen_> ack fantasai <miriam> q+ <TabAtkins> fantasai: The problem I noticed reading thru the thread is the bracketed syntax was represented as always putting in the ampersand, while the point of bracketed syntax was to avoid needing it <TabAtkins> fantasai: So I dont' think that's true that it was fairly written <TabAtkins> TabAtkins: I didn't take that as part of the syntax at all, still needed the & to be the nesting selector <TabAtkins> fantasai: The point was that it was mostly never needed, so I think the poll wasn't valid <TabAtkins> miriam: I reviewed the article and helped come up with the syntax, so... <Rossen_> ack miriam <TabAtkins> miriam: I didn't think of removing the & as the main advantage of the brackets <TabAtkins> miriam: As I was writing it I foudn the brackets more confusing than expected, and I actually *added* ampersands for clarity <TabAtkins> miriam: AFter writing the demos I actually changed my mind away from bracketing <TabAtkins> fantasai: I'm not going to block, if y'all liek this syntax better that's fine. I just don't like having an inconsistent syntax. <TabAtkins> fantasai: I just don't think it's fair to say it was overwhelmingingly in one direction <TabAtkins> Rossen_: So it sounds like we have a pretty strong response. Possibly biased, but Mia makes an argument for it not being so. <fantasai> I really don't like the inconsistency, where you have to know when to use @nest <Rossen_> q? <TabAtkins> Rossen_: So proposed resolution is to undo the resolutions from november and leave the spec as it currentyl is <TabAtkins> Rossen_: Any additional comments or feedback? <TabAtkins> fantasai: I'm not gonna object because everyone seems to like it, but I really don't like that authors have to know when to use @nest, it's not a consistent syntax <TabAtkins> fantasai: And I wish we had something else besides an inconsistent syntax <TabAtkins> fantasai: I don't like @nest everywhere since it's verbose, but... <TabAtkins> miriam: I agree on the problem and it's why I liked bracket before. I agree it's an issue and it's weird <TabAtkins> miriam: But once I started writing examples, it almost all basic use-cases you just use the & and it works and looks cleaner. <TabAtkins> miriam: and there are only rare cases where you need to use @nest and it's not as hard as I initially thought to know the difference <TabAtkins> miriam: So that's why I changed my mind even tho I agree it's inconsistent <bradk> +1 to miriam <TabAtkins> plinss: I think elika has a valid point and before we decide on this we could make another design phase to try to come up with another route. <fantasai> I'm not so opposed to the & , just that some rules require @nest and others dont' <TabAtkins> plinss: I'm okay with undoing the previous resolution, just not sure we shoudl resolve affirmatively on the current syntax <TabAtkins> Rossen_: Right, so let's just resolve on undoing the previous resolution. <TabAtkins> Rossen_: Objections? <TabAtkins> RESOLVED: Revert the previous resolution from Nov 2021 mandating bracket-nesting syntax, and the WG preference for a single nesting syntax. <TabAtkins> Rossen_: And for the record, want to strongy encourage continued improvement of the design. |
Didn't see it come up in the minutes but I think the results of any poll directed at stylesheet authors will be biased when it comes to nesting. In this case they were given the choice between the syntax that they use today with postcss-nesting or two alternatives. They will always pick the one that they use today. If they were given the choice for sass-like nesting they would have picked that because it is still the mental model for nesting for the majority of authors. This is the downside of pushing out polyfills for features that aren't stable even in spec form. In meaningless extra data points : It seems that there were some question about |
I'm surprised the "parser switch" option suggested by LeaVerou got so lost in the mix. I actually liked that option a lot. It doesn't have the inconsistencies of knowing where to do what, it doesn't have the double indention and it doesn't require continuous repetition of the button {
font-size: inherit;
color: white;
background: blue;
border: none;
@nest;
&.btn-special {
color: yellow;
background: gold;
@nest;
&.btn-large { font: 3rem / 1.5 fantasy, sans-serif; }
}
menu & {
text-decoration: underline;
}
} This is
I struggle to see the issues with this. The CSSOM would be relatively simple, leaving out the Anyway, I just wanted to pull this option out of the dark in case it was overshadowed by the other three options. Would love to hear what people's opinions are on this one. |
I assume that a simple child selector would not need the ampersand? If so, I love it. To convert existing SCSS to this (assuming the SCSS author isn't an absolute monster who mixes styles with nested selectors), one would simply add /* ------------------ */
@nest;
/* ------------------ */
/* or perhaps just */
@nest; /* ------------------ */ Perhaps for the sake of those cases where the CSS cannot be fully controlled by authors (restrictive CMSes or whatever), an optional The only problem I can see with this syntax is the inconsistency of how CSS uses @something (parameters) {
/* stuff */
} ...which, bringing this right back around to the very starting post, is why I suggested the Granted, even the current spec of |
Indeed, an ampersand wouldn't be required. I would write it anyway, and we can require it if we want to, but the Not sure if an The inconsistency about at-rules is valid, but only to a certain extent; CSS already has things like |
Having a parser switch like that would be unprecedented in CSS; the closest thing we have to it is the @charset syntax, which also looks like an at-rule but isn't and has effects on how we parse the rest of the document. Other things like @import or @layer statements aren't similar; they're just normal at-rules that happen to not need a block. The parser doesn't need to know a thing about them. |
LeaVerou suggested even more interesting idea which would allow to use any nested rule starting from |
Yes, but nesting itself would be unprecedented too. Perhaps the tradeoff is worth it? As for the comparisons to Anyway, in an attempt to push this issue ever so slightly closer to resolved, I'd like to logically go through the options we have, with the assumption that we do not want to introduce new concepts such as parser switches. Of course, for this syntax to be usable, it necessarily needs to be somewhat resembling SCSS nesting. People have been using that for a very long time, so it is familiar to them, and it's what they want in CSS. Now, let's say we have the following SCSS: button {
color: red;
aside & { color: blue; }
} In CSS, we will need to somehow distinguish the My point here is, we can try to think of different syntaxes all day long, but the viable candidates are extremely limited when it comes to syntax structures we already have, especially given constraints like "no lookaheads" and "no inconsistencies". The parser switch would be a massive change for parsers, yes. If implementers are against it, I would understand. But I haven't seen an implementer actually object to the idea. In my opinion, the |
The more I look at the current syntax, the more convinced I am that there's no 'inconsistency' to worry about, and the 'double syntax' pretty cleanly reflects two different tools that are both useful in nested CSS. And these two types of nesting are actually pretty clear to authors:
Sass has a very similar special-nesting feature in the While it's true that CSS has to do this for obscure internal parsing reasons, the resulting logic is not at all obscure, or arbitrary, or difficult to learn & teach. It's actually more clear than the Sass logic, since it aligns more directly with a clear semantic difference that authors already think of as two different 'types' of nesting: appending vs manipulating. |
My confusion/concern with the nesting syntax was always the lack of clarity caused by having "anonymous" blocks that just added extra braces for no clear-to-the-user purpose. I think that if the poll had included an option that required figure {
margin: 0;
@nest {
> figcaption {
background: lightgray;
@nest {
> p {
font-size: .9rem;
}
}
}
}
} it still has the double-indent issue, but I think it resolves a lot of the clarity/ambiguity issues that really turned me against the brackets proposal initially. |
Closing as resolved. |
This is kind of a follow-on from the closed issue #2701 and #2937.
Would it make any sense to implement native CSS nesting something like this?
Or perhaps even like this (uses existing parser patterns):
Basically, it would require any nesting to be placed within a separate grouping container, as a way to differentiate in bulk between attributes and nested selectors. Seems to me that this would have the benefit of not using the ampersand character and thus avoiding complications with pre-processors...
The text was updated successfully, but these errors were encountered: