- From: fantasai <fantasai.lists@inkedblade.net>
- Date: Tue, 23 May 2023 01:33:17 -0400
- To: W3C style mailing list <www-style@w3.org>
========================================= These are the official CSSWG minutes. Unless you're correcting the minutes, Please respond by starting a new thread with an appropriate subject line. ========================================= CSS Nesting ----------- Clearer definition of "nest-containing" https://github.com/w3c/csswg-drafts/issues/7972 - RESOLVED: Relative-ness is decided based on & appearing *anywhere* in the selector, even nested inside anything else - RESOLVED: serialize any invalid :is() branch that contains `&` as-is - Emilio opened https://github.com/w3c/csswg-drafts/issues/8356 about preserving invalid selectors in :is() and :where() CSSOM for nested media query rules https://github.com/w3c/csswg-drafts/issues/7850 - RESOLVED: Property declarations nested directly into a conditional rule get auto-wrapped in a `& { ... }` style rule. - Group discussed three options for handling interleaved declarations: 1. Similar to style-rules-in-style-rules, treat all nested props as if they occurred together at the top, before any other rules. 2. Wrap properties in the default-style-rule where they stand, letting them interleave. 3. Treat this as invalid (presumably dropping properties that occur after rules?) - The first option means interleaved declarations get reshuffled in the cascade, which breaks the "last declaration of equal specificity wins" expectation. - Group also discussed how the .style option would map under each of these options. There was some concern about trying to keep behavior consistent between conditional rules and style rules. One proposal was mapping .style to the first & { ... } rule, or to one that represents only declarations ahead of the first style rule. - Discussion will continue in the issue. ===== FULL MEETING MINUTES ====== Agenda: https://lists.w3.org/Archives/Public/www-style/2023Jan/0011.html Log: https://www.w3.org/2023/01/25-css-irc Present: Patrick Angle(?) Rossen Atanassov Tab Atkins David Baron Oriol Brufau Elika Etemad Chris Lilley François Remy Miriam Suzanne Lea Verou (via IRC) Scribe: fantasai CSS Nesting =========== Administrative -------------- Swapping order of 2 & 3 on agenda. Adding republication as a topic Clearer definition of "nest-containing" --------------------------------------- github: https://github.com/w3c/csswg-drafts/issues/7972 TabAtkins: If a selector starts with a combinator, we assume relative selector: insert & and interpret accordingly TabAtkins: If no such combinator, we need to check if this should be a relative selector or if there's an & deeper in TabAtkins: Could have something like "foo &" or ":not(&) ..>" TabAtkins: But spec is not super clear what it means by "contain" & TabAtkins: e.g. what happens if & is inside :is() within branch that's ignored by invalidity? <TabAtkins> :is(&:unknown, .foo) TabAtkins: is that &-containing or not? TabAtkins: Depending on our answer, how does it affect serialization? TabAtkins: Listed a few options TabAtkins: 1. Only look at top-level, not inside parens TabAtkins: 2. Look everywhere except inside forgiving parsing list TabAtkins: 3. Get deeper into parsing guts, say something about & inside even forgiving selector lists that might be invalid, and preserve that info across serialization even though normally drop such branches during reserialization TabAtkins: fantasai and I thought #3 would be weird, because [missed] TabAtkins: so initial suggestion was #1, simple suggestion TabAtkins: after discussion with Natalie and Miriam, there were too many use cases for & deeper than top-level TabAtkins: and having selector only in one branch and not others is weird and unusual, so not that important if behavior differs in branches TabAtkins: so suggestion is to look for & in every branch, whether invalid or not TabAtkins: needs authors to be a little more careful TabAtkins: but simple: if there's an & anywhere, it's got one TabAtkins: Might need to tighten up parsing by having known-invalid selectors serialize as something particular TabAtkins: So definition proposed is that it's containing an & if it contains an & anywhere fremy: Tab mentioned that it's weird to have branches where some contain & but some do not fremy: I agree, if you're doing that it's probably a mistake fremy: But then why can't we make this a rule? fremy: if you have a branching selector, must have & in every branch fremy: otherwise invalid TabAtkins: Valid question, we could, but probably doesn't matter because unusual case TabAtkins: so I don't think it's necessary TabAtkins: but I could do either way fantasai asks for an example of where this differs TabAtkins: Consider the previous example, where only one branch of :is() has an &. Difference is we'd make that illegal <TabAtkins> so `:is(&:unknown, .foo)` would be an invalid selector TabAtkins: Also weird, it would be the only way for :is() to invalidate a selector TabAtkins: another little bit of complexity that we could avoid for a corner case fremy: Yes, I think I agree. It might not make much sense, but you might want it <lea> in case it helps, as an author, I'd expect .foo { :is(&, .bar) {} } to be interpreted as .foo, .foo .bar , not .foo, .bar TabAtkins: Lea posts a different suggestion, which is that you take different paths in deciding relative selector or not TabAtkins: Problem is that doesn't map directly to :is() notation, so suspect it would complexify implementations fantasai: Do we have any comments from implementers on lea's question? TabAtkins: I can't say for sure because our implementers aren't on the call, but I think our implementation would find it difficult emilio: Doing what Lea suggests would be fairly annoying emilio: and could have problems with selectors growing exponentially, which is not great emilio: I think this is a problem Blink had when they initially implemented this TabAtkins: When trying to do naïve impleementation of :is(), it was a problem <fremy> @ lea, the question was whether TabAtkins's reply to you addressed your concern or not <fremy> @ lea, and (I guess) whether this was something you felt strongly about it <lea> fremy: no, this is not something I feel strongly about <lea> though I do find it fairly weird if you can "break" out of the nest by doing `:is(&, .bar)`. Also if :is(.bar) matches different things than the .bar in :is(&, .bar) Rossen: Current proposal is simply option 3 without any further modification (and by option 3 we mean option 3 in IRC, which is option 2 in the issue) <TabAtkins> proposed resolution: Relative-ness is decided based on & appearing *anywhere* in the selector, including in (potentially-invalid) branches of a forgiving-selector-list plinss: I presume it's only legal for one & in selector? TabAtkins: no can have multiple plinss: so I can say &.foo &.bar? TabAtkins: & represents elements matched by the parent selector TabAtkins: would be two different elements that both happen to match the same parent plinss: fair enough fantasai: suggest we clarify that if I have any kind of selector garbage inside :is(), including an & nested deep inside of the garbage, that & is considered to be an & for these purposes? TabAtkins: correct <fantasai> :is(:unknown(&)) <fantasai> :is([something (&something else { } ) ]) <TabAtkins> :is([{(&)}]) has an ampersand in it, per the suggested resolution TabAtkins: If during selector parsing you see an & delim token, mark the selector as having an & and continue normal parsing Rossen: any other feedback? RESOLVED: Relative-ness is decided based on & appearing *anywhere* in the selector, even nested inside anything else TabAtkins: Question is what to do about serialization TabAtkins: Option 1: do nothing special. This might mean that an invalid branch is dropped, which changes selector interpretation TabAtkins: Option 2: serialize any :is() branch that contains an & as-is TabAtkins: Option 3: serialize any :is() branch that contains & as something guaranteed to be invalid, e.g. "&:not(&)" (which is guaranteed to not match but definitely contains an &) emilio: Only doing [missed] TabAtkins: Option 4: do 2 or 3 if that's the only branch that contains an & (and thus the selector interpretation is dependent on that branch existing) emeyer: wrt option 1, what are the concerns? emilio: It's bad if something serializes and then when you re-parse it it's interpreted differently than the original TabAtkins: So you can have a selector that's relative in new browsers and non-relative in older browsers, which is a significant change emilio: You lose the forgiveness ... which is annoying [missed] emilio: Implementation-wise, it feels like it would be nice not to account for & in invalid branches, so you can compute the relativeness of a selector independently of parsing process emilio: ... but all the options are weird in their own way <dbaron> My preferences: definitely not option 1, weakly prefer option 3 (without the option 4 variant). <fantasai> +1 dbaron <fremy> @ TabAtkins, for option 3 `&:not(&)` might be costly to compute, how about something truly invalid like `::-invalid-(&)` [Tabatkins summarizes dbaron's comment into audio] plinss: [asks about option 2] emilio: option 2 is consistent with media queries, where we preserve the CSS string for general enclosed TabAtkins: fair [Leaning towards 2 or 3] <dbaron> I'm also fine with 2. Straw Poll: Option 2 vs Option 3 <TabAtkins> abstain abstain (defer to implementers) <emilio> either wfm <plinss> 2 <lea> 2 (based on the descriptions in the comment primarily, as I've missed a lot of the discussion due to being on another call) <Rossen> 3, 2 <fremy> abstain <emeyer> abstain <chris> abstain <davidleininger> 2 <dbaron> abstain <florian> abstain <jensimmons> abstain emilio: other issue of option 2 is that it changes behavior from today TabAtkins: in theory, yes, if someone is putting & inside their invalid :is() selector, it will change serialization emilio: We probably want to do it for all invalid selectors, so that :unknown is reserialized as-is, regardless of & TabAtkins: I suggest taking up separately Proposed to go with option 2 Emilio to open issue about switching serialization for all selectors plinss: In my mind, would want to be consistent for all selectors TabAtkins: I agree in theory, don't care too much RESOLVED: Option 2 TabAtkins: open separate issue for the rest, because it may have a compat risk plinss: If the other one goes the other way, might want to revert this one TabAtkins: currently don't have compat either way for & stuff plinss: Yes, but I want us to note in the other issue to reconsider this one <emilio> I can open the issue ACTION: emilio to open new issue <emilio> https://github.com/w3c/csswg-drafts/issues/8356 CSSOM for nested media query rules ---------------------------------- github: https://github.com/w3c/csswg-drafts/issues/7850 TabAtkins: Spec as currently defined allows you to nest conditional rules and other rules directly inside a style rule TabAtkins: in that context, they can have properties directly in them TabAtkins: so e.g. div { ... @media something { color: blue; } } TabAtkins: question is, how do we reflect this in the OM TabAtkins: CSSMediaRule doesn't have .style TabAtkins: One possibility is to add .style TabAtkins: other possibility is treat such properties as wrapped in & { .. } TabAtkins: They would be exposed in their current OM TabAtkins: I suggest going with #2, because then we don't need to change the OM for any of these rules TabAtkins: And also, if you're clearing out an at-rule, currently you can just replace .cssRules but if we add .style then you have to remember to clear out .style also TabAtkins: So that's my proposal <fremy> +1 to the second option TabAtkins: wrap directly-contained properties with `& { ... }` dbaron: Is there a way to distinguish these rules from real ones written that way? TabAtkins: No, and we have a followup issue to discuss whether they reserialize out with the & { ... } wrapper TabAtkins: other than that no author-visible difference lea: I think option 2 is fine for reading code, but could we author a shortcut for this sort of thing, e.g. adding .style to the OM lea: to provide a shortcut for creating this rule if it doesn't exist lea: if you read back .cssRules, it's fine to have the extra rule TabAtkins: setting .style sometimes creating a new rule is a bit magic <emilio> +1 tab TabAtkins: for any other case would have to manually construct the rule anyway? So I don't think it's worthwhile to have the additional magic for this case Rossen: Lea, what you're suggesting can be added later lea: yes, that's why I don't feel too strongly about it dbaron: When I asked if they were distinguishable, I felt they should be distinguishable somehow dbaron: Seems nice if can reserialize in original form dbaron: also nice to distinguish TabAtkins: other than serialization, why distinguish? dbaron: can't think of anything dbaron: if there was an accessor on the container for this kind of thing ... dbaron: then you could compare to see if it's different dbaron: it would also let you do what lea was asking with a single additional access, if the access implicitly created it, though maybe that's a bad idea emilio: Is this rule always need to be the first one? TabAtkins: that's a great question, I don't have a strong opinion TabAtkins: e.g. do properties outside rules all glom together ? TabAtkins: I don't have a strong opinion patrickangle: Going back 1 step, we'll need to be able to distinguish for developer tools anyway, so will have to support in the engine even if not Web-exposed fantasai: question of whether these glom together or not is pretty important b/c it changes the cascade implications fantasai: can have a selector that has the same specificity as the &, so if you have interleaved declarations and they all have the same specificity, where they occur in order is going to make a difference as to the output of the cascade <miriam> +1 - I don't think declarations should get merged like that fantasai: so whether declarations get glommed together and shifted to the top is something that needs to be definitely decided fantasai: because that impacts the cascade <fremy> I would not support "moving to the top" the declarations fantasai: I think we should do whatever we do outside of conditional rules fantasai: so if outside this context we glom them all together and handle them as occurring before all other rules, then we should do the same thing inside of conditional rules fantasai: And if it's not, we shouldn't TabAtkins: We do indeed effectively glom all the un-nested declarations together outside of conditional rules, so I suggest we glom them together fremy: I don't like that. I prefer in both cases we create an & rule <TabAtkins> today, in `.foo { color: red; & { ... } width: 100px; }`, we treat it as equivalent to `.foo { color: red; width: 100px; & {...}}` TabAtkins: First proposed resolution, properties in a default style rule get auto-wrapped in `& { ... }` Rossen: objections? RESOLVED: properties in a conditional rule get auto-wrapped in `& { ... }` TabAtkins: For declarations which are not at the top, do we do the same thing as style rules or do we wrap each bunch independently or move them all to the top? fremy: another option is to make them invalid fremy: idk if worth considering fremy: I find it pretty confusing that you can write something after something else, and it's treated as coming before <TabAtkins> 1. Similar to style-rules-in-style-rules, treat all nested props as if they occurred together at the top, before any other rules? <TabAtkins> 2. Wrap properties in the default-style-rule where they stand, letting them interleave. <TabAtkins> 3. Treat this as invalid (presumably dropping properties that occur after rules?) matthieudubet: If serialize with .cssText, then [could be confusing] * scribe sorry, missed half that last comment :( TabAtkins: for .cssText, I would serialize in canonical form TabAtkins: assuming we glom together, then if first rule in your .cssRules is & by itself, then we serialize that as naked properties matthieud: That's what we assume, but then ppl write declarations and then [missed] matthieud: but maybe it's not an issue? TabAtkins: My proposed resolution is that we treat interleaved declarations the same way in style rules, i.e. they are all sorted to the top fremy: Can we resolve to do exactly as for style rules, but then discuss if that's what we actually want for style rules? TabAtkins: works for me <miriam> +1 Proposed resolution: do exactly as for style rules wrt sorting of declarations interleaved with style rules fantasai: the reason we didn't do it for style rules is we don't have OM ability to support that for style rules fantasai: the expectation is that all declarations would be in .style property, and to do that we can't care about their order wrt .cssRules fantasai: So worth pointing out that a .cssRules in a style rule will not contain the naked properties fantasai: So that's different from in conditional rules, which is a little inconsistent. fantasai: So do we really want that inconsistency, or do we want to do something like Lea's proposed magic, or...? fantasai: but it is inconsistent TabAtkins: I would have preferred something like this resolution for style rules as well, but for clear legacy reasons we can't do that for style rules -- authors need to be able to manipulate in .style TabAtkins: but we need to make sure that the cascade is consistently handled fantasai: one thing we could do to keep it consistent is to have .style represent the first &-selector'd rule inside of .cssRules fantasai: and then do that for both style and conditional rules fantasai: And all subsequent groups of declarations would each get their own &-rule that would *not* be accessible via .style fantasai: And that would give the same interface for both. It would keep the interleaved order of decls. fantasai: Not sure if that's what we want, but it would give consistent OM & cascading while allowing preserving interleaved order. <fremy> (what fantasai just described is my preference) plinss: [something] TabAtkins: Yes plinss: An alternative is, inside conditional rules all properties are wrapped in an &-rule, but in a regular style rule the *first* group of properties is not, but later ones do plinss: so in `.foo { color: red; &{...} color: blue; }`, it's equivalent to `.foo { color: red; &{...} &{color: blue;}}` emilio: perf-wise that seems slightly problematic emilio: not quite clear how it would work with shorthands emilio: I think coalescing them makes sense emilio: otherwise using shorthands becomes different inside a conditional rule vs in a style rule emilio: I'm not a fan of wrapping everything inside of different rules - it means now you have to selector-match more times <fremy> I am not sure I understood that, but we are out of time, so we should clarify on thread Rossen: Objections? fantasai: Seems like there are more things to consider than we thought at the start, wouldn't be good to resolve yet Rossen: Okay, take the rest to the thread
Received on Tuesday, 23 May 2023 05:33:31 UTC