You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[css-nesting-1] CSSOM for nested media query rules #7850
Specifically, where does the “color: green;” go? It cannot be on the media query rule, because those do not support containing CSS properties. One could say there's an implicit & {} CSSStyleRule (the example says the syntax is “equivalent” to that, though the standard text does not specify it, just that the properties “[apply] to the same elements as the parent rule”), but if so, would the “& span” rule be a subrule of that implicit & {} rule, or would it lie under the media query rule? How would it serialize?
What about if the “color: green” is removed, so there are no properties under the @media-query? Would that change anything, or would there still be a & {} rule?
I'm wondering if we should say something like: If the first non-comment token in a @media (or @supports, etc.) block is not a & or an at-rule, the entire block is implicitly wrapped in & {}. I don't have strong opinions on whether that & {} should be removed again in serialization or not.
Naive questions: why CSSMediaRule.style could not be defined (if the media rule is not in a style rule, declarations must be ignored), and should it be possible (and how) to declare a property value via CSSOM for a media rule nested in a style rule?
It could, but you'd end up with a pretty weird un-orthogonality, if you suddenly have rules that are to be ignored (but otherwise correctly parsed, visible through CSSOM etc.). You could of course declare something like that rules within @media at the top level have the same effect as being within the universal selector, but at that point, you should probably also declare that naked properties at the top should, and all of this sounds very much like something that should be done in the MQ spec, not in nesting…
you suddenly have rules that are to be ignored (but otherwise correctly parsed, visible through CSSOM etc.)
(Sorry,) what rules should suddenly have to be ignored? I only asked if it could be defined that declarations specified via CSSMediaRule.style could be ignored when the context of CSSMediaRule is not <style-block>.
Rules which are invalid according to their context should also be ignored when specified via CSSMediaRule.cssText though (on setting the cssText attribute must do nothing), but I think ignoring rules which are invalid according to their context is already defined normatively in CSS Syntax, and in the corresponding specs for the rules. For a conditional rule, in CSS Nesting:
[...] this specification allows nested conditional group rules inside of style rules. When nested in this way, the contents of a conditional group rule are parsed as <style-block> rather than <stylesheet>
If you meant declarations that are to be ignored, I think anything other than an at-rule appearing in a list of rules (<stylesheet>) is not ignored but consumed until the parser finds a simple block.
/* top level */@media {
property: value; /* consumed as the prelude of... */
{} /* ... a (invalid) qualified rule */@media {} /* parsed */
}
That is what the spec wants. I have no opinion as to whether this is ideal or not, but I can imagine this can be a pain depending on the CSS parser implementation.
It'll go on a .style in CSSMediaRule (not yet defined by the spec).
You want to parse @media differently depending on the context it's in? I'm not sure if that's ideal either.
Yeah, that's the intent of the spec - top-level @media parses its body as a top-level stylesheet, while nested @media parses its body as a nested style rule's body.
...If the first non-comment token in a @media (or @supports, etc.) block is not a & or an at-rule, the entire block is implicitly wrapped in & {}. I don't have strong opinions on whether that & {} should be removed again in serialization or not.
Not defined in either. Which it lives in when it is defined is just a question of what's more convenient.
What happens if you set e.g. .style.color on a CSSMediaRule that's not nesting?
Not defined yet, but most likely the result will be "throws an error upon setting" (or possibly "silently ignores the set", since the CSSOM does a lot of silent ignoring).
We implemented the rule of implicit & {} in Blink, and I think the end result is good enough that the spec should strongly consider adopting it. It gave us:
Free reuse of the style declaration parser for this case
Instant disallowing of @layer and other relevant rules in indirectly nested context (save for in CSSOM inserts, which we'd need to check anyway)
Zero CSSOM extensions required
Instantly correct invalidation
No new inconsistent cases around top-level @media
Instantly correct handling of order tiebreak between properties and subrules
And most of all, no need to insert such a rule internally when constructing rule sets for matching/applying (which we'd otherwise probably need to do)
The single thing that's “odd” is that when you serialize (or peek into the rules with CSSOM), you don't get back what you wrote the first time. But this isn't new, and the serialization part could be fixed by removing & {} if it's the first rule if we really care.
I’m fine with implicitly wrapping with & {} if it makes it easier to implement, but it should be removed in serialization / CSSOM per our current principle about serialization being the shortest equivalent syntax (if original syntax cannot be preserved).
Though we should explore if that would cause issues when using @media in @scope. Does wrapping in & {} there (which is equivalent to wrapping in :scope {}) change the meaning? These should be consistent.
I’m fine with implicitly wrapping with & {} if it makes it easier to implement, but it should be removed in serialization / CSSOM per our current principle about serialization being the shortest equivalent syntax (if original syntax cannot be preserved).
I'm fine with such a rule. Say something like “if the first sub-rule is & {…}, remove that in serialization”.
Though we should explore if that would cause issues when using @media in @scope. Does wrapping in & {} there (which is equivalent to wrapping in :scope {}) change the meaning? These should be consistent.
You mean something like this, or am I misunderstanding you?
@scope (from: .foo) and (to: .bar) {
div {
@media {
color: red; // Would be wrapped in & {}
}
}
}
In implementing this, I realized that we actually don't have any way of serializing conditional group rules that contain declarations at all. So I wrote up a spec that's intentionally very similar to the spec for serializing style rules with nested rules, and it's what I implemented in Blink. The example is for @media, but by replacing steps 1–2, we specs for @supports and so on.
Note that the current spec specifies that an empty div rule should serialize as div { }, but an empty @media rule should serialize as @media screen {\n}. I've kept this as-is, so that existing serialization doesn't change.
Let s initially be the string "@media", followed by a single SPACE (U+0020).
Append the result of performing serialize a media query list on rule’s media query list to s.
Append a single SPACE (U+0020) to s, followed by the string "{", i.e., LEFT CURLY BRACKET (U+007B).
If there is at least one rule in the rule's cssRules list, and the first rule is a CSSStyleRule with a single selector that would serialize to exactly “&”, and that rule has no children:
4.1. Let decls be the result of performing serialize a CSS declaration block on the first rule’s associated declarations.
4.2. Let rules be the result of performing serialize a CSS rule on each rule in the rule’s cssRules list except the first, or null if there are no such rules.
4.3. If rules is null:
4.3.1. Append a single SPACE (U+0020) to s.
4.3.2. Append decls to s.
4.3.3. Append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)).
4.3.4. Return s.
4.4. Otherwise:
4.4.1. Prepend decls to rules.
4.4.2. For each rule in rules:
4.4.2.1. Append a newline followed by two spaces to s.
4.4.2.2. Append rule to s.
4.4.3. Append a newline followed by RIGHT CURLY BRACKET (U+007D) to s.
4.4.4. Return s.
Otherwise:
5.1. Append a newline to s.
5.2. Append the result of performing serialize a CSS rule on each rule in the rule’s cssRules list to s, separated by a newline and indented by two spaces.
5.3. Append a newline to s, followed by the string "}", i.e., RIGHT CURLY BRACKET (U+007D)
If there's already a style rule there, yeah, you can just set a property. If not, you first construct a CSSStyleRule and then .insertRule() it into the @media rule.
If there's already a style rule there, yeah, you can just set a property. If not, you first construct a CSSStyleRule and then .insertRule() it into the @media rule.
It would be nice if the details of how this syntax sugar is resolved are as transparent as possible for authors. Basically, can we go with an option 2 and make it look like option 3 as much as possible? E.g. could we hang a .style on CSSMediaRule et al to provide a shortcut for creating this rule if it doesn't exist?
The CSS Working Group just discussed CSSOM for nested media query rules, and agreed to the following:
RESOLVED: properties in a conditional rule get auto-wrapped in `& { ... }`
The full IRC log of that discussion
<fantasai> Subtopic: CSSOM for nested media query rules
<fantasai> github: CSSOM for nested media query rules
<fantasai> github: https://github.com//issues/7850
<fantasai> TabAtkins: Spec as currently defined alllows you to nest conditional rules and other rules directly inside a style rule
<emilio> https://github.com//issues/8356
<fantasai> TabAtkins: in that context, they can have properties directly in them
<fantasai> TabAtkins: so e.g. div { ... @media something { color: blue; } }
<fantasai> TabAtkins: question is, how do we reflect this in the OM
<fantasai> TabAtkins: CSSMediaRule doesn't have .style
<fantasai> TabAtkins: One possibility is to add .style
<fantasai> TabAtkins: other possibility is treat such properties as wrapped in & { .. }
<fantasai> TabAtkins: They would be exposed in their current OM
<fantasai> ->
<fantasai> -> https://github.com//issues/7850
<fantasai> TabAtkins: I suggest going with #2, because then we don't need to change the OM for any of these rules
<Rossen_> q?
<fantasai> 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
<fantasai> TabAtkins: So that's my proposal
<fremy> +1 to the second option
<fantasai> TabAtkins: wrap directly-contained properties with `& { ... }`
<lea> q?
<lea> q+
<fantasai> dbaron: Is there a way to distinguish these rules from real ones written that way?
<fantasai> TabAtkins: No, and they would reserialize out with the & { ... } wrapper
<Rossen_> ack lea
<fantasai> TabAtkins: other than that no author-visible difference
<dbaron> s/they would reserialize/we have a followup issue to discuss whether the reserialize/
<fantasai> lea: I think option 2 is find for reading code, but could we author a shortcot for this sort of thing, e.g. adding .style to the OM
<fantasai> lea: to provide a shortcut for creating this rule if it doesn't exist
<fantasai> lea: if you read back .cssRules, it's fine to have the extra rule
<fantasai> TabAtkins: setting .style sometimes creating a new rule is a bit magic
<emilio> +1 tab
<fantasai> 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
<fantasai> Rossen_: Lea, what you're suggesting can be added later
<fantasai> lea: yes, that's why I don't feel too strongly about it
<fantasai> dbaron: When I asked if they were distinguishable, I felt they should be distinguishable somehow
<fantasai> dbaron: Seems nice if can reserialize in original form
<fantasai> dbaron: also nice to distinguish
<fantasai> TabAtkins: other than serialization, why distinguish?
<fantasai> dbaron: can't think of anything
<fantasai> dbaron: if there was an accessor on the container for this kind of thing ...
<fantasai> dbaron: then you could compare to see if it's different
<fantasai> dbaron: though maybe that's a bad idea
<emilio> q+
<fantasai> Rossen_: sounds like you talked yourself out of it
<patrickangle_> q+
<Rossen_> ack emilio
<fantasai> emilio: Is this rule always need to be the first one?
<fantasai> TabAtkins: that's a great question, I don't have a strong opinion on it
<fantasai> TabAtkins: e.g. do properties outside rules all glom together ?
<fantasai> TabAtkins: I don't have a strong opinion
<dbaron> s/though maybe that's a bad idea/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/
<Rossen_> ack patrickangle_
<fantasai> 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
<Rossen_> ack fantasai
<Zakim> fantasai, you wanted to point out cascade issues
<TabAtkins> fantasai: question of whether these glom together or not is pretty important bc it chagnes the cascade implications
<TabAtkins> fantasai: can have a selector that has the same specificity as the &, so if you have interleaved decls and they all have the same specificity, where they occur in order is gonna make a difference as to the output of the cascade
<miriam> +1 - I don't think declarations should get merged like that
<TabAtkins> fantasai: so if decls get glommed together and shifted to the top is something that needs to be definitely decided
<fantasai> fantasai: because that impacts the cascade
<fremy> I would not support "moving to the top" the declarations
<TabAtkins> fantasai: I think we should do whatever we do outside of conditional rules
<TabAtkins> fantasai: so if outside this context we glom them all together and handle them as occuring before all other rules, then we should do the same thing insdie of conditional rules
<TabAtkins> fantasai: And if it's not, we shouldn't
<Rossen_> q?
<fantasai> TabAtkins: We do indeed effectively glom all the un-nested declarations together outside of conditional rules, so I suggest we glom them together
<fantasai> 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; & {...}}`
<fantasai> TabAtkins: First proposed resolution, properties in a default style rule get auto-wrapped in `& { ... }`
<fantasai> Rossen_: objections?
<fantasai> RESOLVED: properties in a conditional rule get auto-wrapped in `& { ... }`
<fantasai> 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?
<fantasai> fremy: another option is to make them invalid
<fantasai> fremy: idk if worth considering
<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?
<fantasai> fremy: I find it pretty confusing that you can write something after something else, and it's treated as coming before
<TabAtkins> 2. Wrap properties in the default-style-rule where they stand, letting them interleave.
<fantasai> ??: If serialize with .cssText, then [missed, could be confusing]
<TabAtkins> 3. Treat this as invalid (presumably dropping properties that occur after rules?)
<TabAtkins> s/??/matthieudubet/
<fantasai> TabAtkins: for .cssText, I woudl serialize in canonical form
<fantasai> TabAtkins: assuming we glom together, then if first rule in your .cssRules is & by itself, then we serialize that as naked properties
<fantasai> matthieud: That's what we assume, but then ppl write declarations and then [missed]
<fantasai> matthieud: but maybe it's not an issue?
<fantasai> 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
<fantasai> fremy: Can we resolve to do exactly as for style rules, but then discuss if that's what we actually want for style rules?
<fantasai> TabAtkins: works for me
<miriam> +1
<fantasai> Proposed resolution: do exactly as for style rules wrt sorting of declarations interleaved with style rules
<Rossen_> ack fantasai
<TabAtkins> fantasai: the reason we didn't do it for style rules is we don't have OM ability to support that for style rules
<TabAtkins> fantasai: the expectation is that all decls would be in a .style property, and to do that we can't carea bout their order wrt .cssRules
<TabAtkins> fantasai: So worth pointing out that a .cssRules in a style rule will not contain the naked properties
<TabAtkins> fantasai: So that's different from in conditional rules, which is a little inconsistent.
<TabAtkins> fantasai: So do we really want that inconsistency, or do we want to do something like Lea's proposed magic, or...?
<fantasai> fantasai: but it is inconsistent
<fantasai> 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
<fantasai> TabAtkins: but we need to make sure that the cascade is consistently handled
<TabAtkins> fantasai: one thing we could do to keep it consistent is to have .style represent the first &-selector'd rule inside of .cssRules
<TabAtkins> fantasai: and then do that for both style and conditional rules
<Rossen_> ack fantasai
<TabAtkins> fantasai: And all subsequent groups of declarations would each get their own &-rule that would *not* be accessible via .style
<TabAtkins> fantasai: And that would give the same interface for both. It would keep the interleaved order of decls.
<fremy> (what fantasai just described is my preference)
<TabAtkins> fantasai: Not sure if that's what we want, but it would give consistent OM & cascading.
<fantasai> s/cascading/cascading while allowing preserving interleaved order/
<fantasai> plinss: [something]
<fantasai> TabAtkins: Yes
<TabAtkins> 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
<TabAtkins> plinss: so in `.foo { color: red; &{...} color: blue; }`, it's equivalent to `.foo { color: red; &{...} &{color: blue;}}`
<TabAtkins> emilio: perf-wise that seems slightly problematic
<Rossen_> q?
<TabAtkins> emilio: not quite clear how it would work with shorthands
<TabAtkins> emilio: I think coalescing them makes sense
<TabAtkins> emilio: otherwise using shorthands becomes different inside a conditional rule vs in a style rule
<TabAtkins> 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
<TabAtkins> Rossen_: Objections?
<TabAtkins> fantasai: Seems like there ar emore things to consider than we thought at the start, wouldn't be good to resolve yet
<TabAtkins> Rossen_: Okay, take the rest to the thread
Appeared to be consensus on "wrap naked properties in a & {...} rule".
Everyone also wanted consistency between the behavior of properties in nested style rules and in nested conditional rules. That is, .foo { color: red; & { width: 100px; } width: 200px; } and .foo { color: red; @media all { width: 100px; } width: 200px; } should be consistent.
Most importantly, consistency in cascade behavior (either width: 100px wins in both examples above, or width: 200px wins in both). (Currently width: 100px wins in nested style rules; the 200px gets pulled up to the front, and the nested rule is considered "after" it.)
Less importantly but not unimportant, consistency in OM behavior.
There were two branches of possible suggestions to address the above (particularly improving the OM consistency):
Add a .style accessor to conditional rules, but somewhat magical - it just routes to the first child rule if it's a "default &{...} rule" as described above (and creates such a rule if one doesn't currently exist).
Change the parsing of style rules so they also wrap their naked properties in &-rules, if they follow other nested rules. (Properties preceding any other rules stay as they are, getting handled by .style rather than a nested rule.)
We ran out of time during the meeting to wrap up this discussion, but there was mild opposition to both of these ideas.
For (1), the thought was that this felt a little too magical, especially with the implicit rule creation+insertion on the first write to .style.
For (2), there was a minor perf concern - this would mean doing more selector matching in this case. There's also a concern about the interpretation wrt shorthands; shorthands + longhands are coalesced in particular ways when they're in the same style block, but would produce a slightly different behavior if spread across multiple style blocks.
I propose in our next take-up of this issue that we resolve to just do the "wrap naked properties in a &{...} rule" part.
[...] This includes in the OM. (That is, the childRules attribute actually starts with this nested style rule, containing all the directly-nested properties.)
The CSSMediaRule object will have a single CSSStyleRule object in its .childRules attribute, containing the grid-auto-flow property.
I guess you mean .cssRules instead of .childRules.
Should this implicit nested style rule always exist when the condition rule is nested, even if it has no declaration? I mean, does CSSMediaRule.cssRules[0] have to be an empty nested style rule?
#parent {
@media {
& { color: green }
}
}
If not, what should happen with CSSMediaRule.cssText += 'color: red'? Should the previously exsting rule (& { color: green }) move to index 1?
Should this implicit nested style rule always exist when the condition rule is nested, even if it has no declaration? I mean, does CSSMediaRule.cssRules[0] have to be an empty nested style rule?
No, it only exists when it needs to exist.
If not, what should happen with CSSMediaRule.cssText += 'color: red'? Should the previously exsting rule (& { color: green }) move to index 1?
Nothing should happen, because the spec defines .cssText as doing nothing when set. ^_^ (It's not technically readonly, in the WebIDL sense, for historical reasons, but it's readonly in practice.)
The CSS Working Group just discussed [css-nesting-1] CSSOM for nested media query rules, and agreed to the following:
RESOLVED: If naked property declarations inside @media become a thing, they are wrapped in a & {} rule.
The full IRC log of that discussion
<fantasai> TabAtkins: last discussed in January
<fantasai> ... basic question was, given that we allow nesting @media rule, and then you can put naked declarations inside, how do we expose that in the OM?
<fantasai> ... currently CSSMediaRule only exposes child rules
<fantasai> TabAtkins: The current proposal is if you have naked properties, they are effectively wrapped in a nested style rule with the & selector
<fantasai> ... and are exposed that way
<fantasai> ... This rule is the first rule in @media's child rules
<fantasai> ... and only exists if it needs to
<fantasai> TabAtkins: there were two further suggestions
<fantasai> ... one was to add .style accessor that roots to this first style rule
<fantasai> ... and/or to change parsing of all nested rules to wrap in & {} as well
<fantasai> TabAtkins: there was mild opposition to both ideas, but ran out of time
<fantasai> ... some concern about adding more rules to large stylesheet
<fantasai> TabAtkins: suggestion that all nested @rules that accept style rules usually, wrap in & {}
<fantasai> astearns: we could do things later?
<fantasai> TabAtkins: we couldn't do the second thing, but adding .style is still possible in the future
<fantasai> plinss: Some concern with ability to put raw declarations inside an @media rule that's nested
<fantasai> ... because we get yet another situation where you can mix declarations and rules, but defaulting differently
<fantasai> TabAtkins: ...
<fantasai> plinss: @media contains rules, so it's in rule error recovery
<fantasai> ... if you have stuff in side ...
<fantasai> TabAtkins: using same error recovery in all places
<fantasai> ... parser will fail rule parsing if it encounters ; before the block
<fantasai> ... today if trying to parse a rule, it will encounter the ;, stop there, throw it out, and then start parsing fresh
<fantasai> TabAtkins: semicolons have never been valid in the front of rules before, no expectation that this would break anything
<fantasai> plinss: if we have declaration top-level with braces, then something that follows braces?
<fantasai> TabAtkins: generally fine, some restrictions that we want to obey for future compatibility concerns, but very minor
<fantasai> ... part of larger issue
<fantasai> ... same effect here as anywhere else
<fantasai> ... won't affect current stuff, because nothing looks like that today
<fantasai> ... and in the future, you'll have the same set of restrictions everywhere, which are extremely minimal
<fantasai> ... but that's the larger parsing issue, same issue; only concern here is whether existing @media would be parsed differently, and I think the answer is "no in practice"
<fantasai> plinss: I accept it's part of the other issue
<fantasai> ... also concerned about older browsers parsing new content
<fantasai> TabAtkins: if you do nesting in an older browser, things will be very screwed up, misparsing is the least of your concerns
<fantasai> astearns: in the interest of moving forward, perhaps we can resovle to wrap things in an implicit & rule if we allow naked declarations inside an @media rule
<fantasai> plinss: I'm ok with that. I'm concerned about requiring &{} wrapping, ok if OM is same either way
<fantasai> RESOLVED: If naked property declarations inside @media become a thing, they are wrapped in a & {} rule.
<fantasai> emilio: Is this anonymous rule well-defined? it's at the beginning?
<fantasai> TabAtkins: details in the issue, always at the beginning if it's needed
<fantasai> plinss: If mix of declarations and rules, all the declarations sort to the beginning?
<fantasai> TabAtkins: yes
<fantasai> matthieudubet: if they actually write & { ... } explicitly, do we group with that?
<fantasai> TabAtkins: no
<fantasai> TabAtkins: if they explicitly write a rule, that's their rule, we don't do anything magic with that
<fantasai> matthieudubet: OK, that's not what Safari Tech Preview is doing right now
<fantasai> ... it tries to show the smallest representation with the same semantics
<fantasai> ... so it removes it
<heycam> @media (...) { & { color: red; } color: green; } /* element would be red */ ?
<fantasai> TabAtkins: This isn't in the spec yet, so it's not tested yet, so Safari will discover if it's doing the wrong thing
<fantasai> emilio: You can't just merge them because the order matters
<fantasai> heycam: So just want to clarify that all the bare declarations get hoisted to the top of the rule
<fantasai> ... so it might be a little confusing, about the order they'll applied
<fantasai> TabAtkins: it's the same behavior you get in ordinary nested style rules, though
<fantasai> ... all your naked declarations are sorted before all of the style rules
<fantasai> fremy: we resolved that we would be consistent, and maybe discuss what to do (but nobody filed an issue about that)
<fantasai> fremy: I think it's weird that they move, but should be consistent
<fantasai> plinss: if I have a bare red and then two wrapped blue, and then bare green, I expect it to be green and it will be blue
<fantasai> fremy: we discussed in the past, if we agree, we should file an issue about that
<fantasai> TabAtkins: this is a minor issue for compat purposes, so we can adjust this
<fantasai> TabAtkins: I think the answer is, don't put naked things after rules; but no reason to ban them
<fantasai> ... either way it's confusing
<fantasai> ... but I don't care which way we go
<fantasai> ... but aside from that, that would be a separate issue, I think we're done with this issue
<fantasai> astearns: resolution makes us consistent for the moment, but we can look at general issue on what to do with general declarations
<fantasai> ACTION: fremy or plinss to file issue about whether naked declarations get sorted to the top of the list or not
Activity
sesse commentedon Oct 8, 2022
I'm wondering if we should say something like: If the first non-comment token in a @media (or @supports, etc.) block is not a & or an at-rule, the entire block is implicitly wrapped in & {}. I don't have strong opinions on whether that & {} should be removed again in serialization or not.
cdoublev commentedon Oct 8, 2022
Naive questions: why
CSSMediaRule.stylecould not be defined (if the media rule is not in a style rule, declarations must be ignored), and should it be possible (and how) to declare a property value viaCSSOMfor a media rule nested in a style rule?sesse commentedon Oct 10, 2022
It could, but you'd end up with a pretty weird un-orthogonality, if you suddenly have rules that are to be ignored (but otherwise correctly parsed, visible through CSSOM etc.). You could of course declare something like that rules within
@mediaat the top level have the same effect as being within the universal selector, but at that point, you should probably also declare that naked properties at the top should, and all of this sounds very much like something that should be done in the MQ spec, not in nesting…cdoublev commentedon Oct 11, 2022
(Sorry,) what rules should suddenly have to be ignored? I only asked if it could be defined that declarations specified via
CSSMediaRule.stylecould be ignored when the context ofCSSMediaRuleis not<style-block>.Rules which are invalid according to their context should also be ignored when specified via(on setting theCSSMediaRule.cssTextthoughcssTextattribute must do nothing), but I think ignoring rules which are invalid according to their context is already defined normatively in CSS Syntax, and in the corresponding specs for the rules. For a conditional rule, in CSS Nesting:If you meant declarations that are to be ignored, I think anything other than an at-rule appearing in a list of rules (
<stylesheet>) is not ignored but consumed until the parser finds a simple block.sesse commentedon Oct 11, 2022
You want to parse
@mediadifferently depending on the context it's in? I'm not sure if that's ideal either.cdoublev commentedon Oct 11, 2022
That is what the spec wants. I have no opinion as to whether this is ideal or not, but I can imagine this can be a pain depending on the CSS parser implementation.
tabatkins commentedon Oct 11, 2022
It'll go on a
.stylein CSSMediaRule (not yet defined by the spec).Yeah, that's the intent of the spec - top-level @media parses its body as a top-level stylesheet, while nested @media parses its body as a nested style rule's body.
sesse commentedon Oct 12, 2022
The MQ spec, or the nesting spec?
What happens if you set e.g. .style.color on a CSSMediaRule that's not nesting?
Myndex commentedon Oct 12, 2022
Hi @sesse
IMO like this, implicitly wrapped if it does not exist, but should probably not be remved in serialization.
At least how I am thinking of the ampersand to be like the
$in a regex rewrite, considering the&as a symbol for the parent selector.tabatkins commentedon Oct 18, 2022
Not defined in either. Which it lives in when it is defined is just a question of what's more convenient.
Not defined yet, but most likely the result will be "throws an error upon setting" (or possibly "silently ignores the set", since the CSSOM does a lot of silent ignoring).
sesse commentedon Oct 21, 2022
We implemented the rule of implicit & {} in Blink, and I think the end result is good enough that the spec should strongly consider adopting it. It gave us:
@layerand other relevant rules in indirectly nested context (save for in CSSOM inserts, which we'd need to check anyway)@mediaThe single thing that's “odd” is that when you serialize (or peek into the rules with CSSOM), you don't get back what you wrote the first time. But this isn't new, and the serialization part could be fixed by removing & {} if it's the first rule if we really care.
LeaVerou commentedon Oct 21, 2022
I’m fine with implicitly wrapping with
& {}if it makes it easier to implement, but it should be removed in serialization / CSSOM per our current principle about serialization being the shortest equivalent syntax (if original syntax cannot be preserved).Though we should explore if that would cause issues when using
@mediain@scope. Does wrapping in& {}there (which is equivalent to wrapping in:scope {}) change the meaning? These should be consistent.sesse commentedon Oct 21, 2022
I'm fine with such a rule. Say something like “if the first sub-rule is & {…}, remove that in serialization”.
You mean something like this, or am I misunderstanding you?
sesse commentedon Oct 24, 2022
In implementing this, I realized that we actually don't have any way of serializing conditional group rules that contain declarations at all. So I wrote up a spec that's intentionally very similar to the spec for serializing style rules with nested rules, and it's what I implemented in Blink. The example is for
@media, but by replacing steps 1–2, we specs for@supportsand so on.Note that the current spec specifies that an empty div rule should serialize as
div { }, but an empty@mediarule should serialize as@media screen {\n}. I've kept this as-is, so that existing serialization doesn't change."@media", followed by a single SPACE (U+0020).4.1. Let decls be the result of performing serialize a CSS declaration block on the first rule’s associated declarations.
4.2. Let rules be the result of performing serialize a CSS rule on each rule in the rule’s cssRules list except the first, or null if there are no such rules.
4.3. If rules is null:
4.3.1. Append a single SPACE (U+0020) to s.
4.3.2. Append decls to s.
4.3.3. Append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)).
4.3.4. Return s.
4.4. Otherwise:
4.4.1. Prepend decls to rules.
4.4.2. For each rule in rules:
4.4.2.1. Append a newline followed by two spaces to s.
4.4.2.2. Append rule to s.
4.4.3. Append a newline followed by RIGHT CURLY BRACKET (U+007D) to s.
4.4.4. Return s.
5.1. Append a newline to s.
5.2. Append the result of performing serialize a CSS rule on each rule in the rule’s cssRules list to s, separated by a newline and indented by two spaces.
5.3. Append a newline to s, followed by the string "}", i.e., RIGHT CURLY BRACKET (U+007D)
19 remaining items
tabatkins commentedon Jan 13, 2023
Yes, so you can't assign an empty array to it, but you can certainly call
.deleteRule()in a loop.SebastianZ commentedon Jan 13, 2023
It sounds like adding a single declaration would then work via
nestedMedia.cssRules[0].style.setProperty(). @tabatkins Correct?If so, I'm for option 2, as it allows to keep the OM for
CSSMediaRuleclean while still providing syntax sugar for CSS.Sebastian
tabatkins commentedon Jan 13, 2023
If there's already a style rule there, yeah, you can just set a property. If not, you first construct a CSSStyleRule and then .insertRule() it into the @media rule.
LeaVerou commentedon Jan 14, 2023
It would be nice if the details of how this syntax sugar is resolved are as transparent as possible for authors. Basically, can we go with an option 2 and make it look like option 3 as much as possible? E.g. could we hang a
.styleonCSSMediaRuleet al to provide a shortcut for creating this rule if it doesn't exist?css-meeting-bot commentedon Jan 25, 2023
The CSS Working Group just discussed
CSSOM for nested media query rules, and agreed to the following:RESOLVED: properties in a conditional rule get auto-wrapped in `& { ... }`The full IRC log of that discussion
<fantasai> Subtopic: CSSOM for nested media query rules<fantasai> github: CSSOM for nested media query rules
<fantasai> github: https://github.com//issues/7850
<fantasai> TabAtkins: Spec as currently defined alllows you to nest conditional rules and other rules directly inside a style rule
<emilio> https://github.com//issues/8356
<fantasai> TabAtkins: in that context, they can have properties directly in them
<fantasai> TabAtkins: so e.g. div { ... @media something { color: blue; } }
<fantasai> TabAtkins: question is, how do we reflect this in the OM
<fantasai> TabAtkins: CSSMediaRule doesn't have .style
<fantasai> TabAtkins: One possibility is to add .style
<fantasai> TabAtkins: other possibility is treat such properties as wrapped in & { .. }
<fantasai> TabAtkins: They would be exposed in their current OM
<fantasai> ->
<fantasai> -> https://github.com//issues/7850
<fantasai> TabAtkins: I suggest going with #2, because then we don't need to change the OM for any of these rules
<Rossen_> q?
<fantasai> 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
<fantasai> TabAtkins: So that's my proposal
<fremy> +1 to the second option
<fantasai> TabAtkins: wrap directly-contained properties with `& { ... }`
<lea> q?
<lea> q+
<fantasai> dbaron: Is there a way to distinguish these rules from real ones written that way?
<fantasai> TabAtkins: No, and they would reserialize out with the & { ... } wrapper
<Rossen_> ack lea
<fantasai> TabAtkins: other than that no author-visible difference
<dbaron> s/they would reserialize/we have a followup issue to discuss whether the reserialize/
<fantasai> lea: I think option 2 is find for reading code, but could we author a shortcot for this sort of thing, e.g. adding .style to the OM
<fantasai> lea: to provide a shortcut for creating this rule if it doesn't exist
<fantasai> lea: if you read back .cssRules, it's fine to have the extra rule
<fantasai> TabAtkins: setting .style sometimes creating a new rule is a bit magic
<emilio> +1 tab
<fantasai> 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
<fantasai> Rossen_: Lea, what you're suggesting can be added later
<fantasai> lea: yes, that's why I don't feel too strongly about it
<fantasai> dbaron: When I asked if they were distinguishable, I felt they should be distinguishable somehow
<fantasai> dbaron: Seems nice if can reserialize in original form
<fantasai> dbaron: also nice to distinguish
<fantasai> TabAtkins: other than serialization, why distinguish?
<fantasai> dbaron: can't think of anything
<fantasai> dbaron: if there was an accessor on the container for this kind of thing ...
<fantasai> dbaron: then you could compare to see if it's different
<fantasai> dbaron: though maybe that's a bad idea
<emilio> q+
<fantasai> Rossen_: sounds like you talked yourself out of it
<patrickangle_> q+
<Rossen_> ack emilio
<fantasai> emilio: Is this rule always need to be the first one?
<fantasai> TabAtkins: that's a great question, I don't have a strong opinion on it
<fantasai> TabAtkins: e.g. do properties outside rules all glom together ?
<fantasai> TabAtkins: I don't have a strong opinion
<dbaron> s/though maybe that's a bad idea/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/
<Rossen_> ack patrickangle_
<fantasai> 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
<Rossen_> ack fantasai
<Zakim> fantasai, you wanted to point out cascade issues
<TabAtkins> fantasai: question of whether these glom together or not is pretty important bc it chagnes the cascade implications
<TabAtkins> fantasai: can have a selector that has the same specificity as the &, so if you have interleaved decls and they all have the same specificity, where they occur in order is gonna make a difference as to the output of the cascade
<miriam> +1 - I don't think declarations should get merged like that
<TabAtkins> fantasai: so if decls get glommed together and shifted to the top is something that needs to be definitely decided
<fantasai> fantasai: because that impacts the cascade
<fremy> I would not support "moving to the top" the declarations
<TabAtkins> fantasai: I think we should do whatever we do outside of conditional rules
<TabAtkins> fantasai: so if outside this context we glom them all together and handle them as occuring before all other rules, then we should do the same thing insdie of conditional rules
<TabAtkins> fantasai: And if it's not, we shouldn't
<Rossen_> q?
<fantasai> TabAtkins: We do indeed effectively glom all the un-nested declarations together outside of conditional rules, so I suggest we glom them together
<fantasai> 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; & {...}}`
<fantasai> TabAtkins: First proposed resolution, properties in a default style rule get auto-wrapped in `& { ... }`
<fantasai> Rossen_: objections?
<fantasai> RESOLVED: properties in a conditional rule get auto-wrapped in `& { ... }`
<fantasai> 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?
<fantasai> fremy: another option is to make them invalid
<fantasai> fremy: idk if worth considering
<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?
<fantasai> fremy: I find it pretty confusing that you can write something after something else, and it's treated as coming before
<TabAtkins> 2. Wrap properties in the default-style-rule where they stand, letting them interleave.
<fantasai> ??: If serialize with .cssText, then [missed, could be confusing]
<TabAtkins> 3. Treat this as invalid (presumably dropping properties that occur after rules?)
<TabAtkins> s/??/matthieudubet/
<fantasai> TabAtkins: for .cssText, I woudl serialize in canonical form
<fantasai> TabAtkins: assuming we glom together, then if first rule in your .cssRules is & by itself, then we serialize that as naked properties
<fantasai> matthieud: That's what we assume, but then ppl write declarations and then [missed]
<fantasai> matthieud: but maybe it's not an issue?
<fantasai> 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
<fantasai> fremy: Can we resolve to do exactly as for style rules, but then discuss if that's what we actually want for style rules?
<fantasai> TabAtkins: works for me
<miriam> +1
<fantasai> Proposed resolution: do exactly as for style rules wrt sorting of declarations interleaved with style rules
<Rossen_> ack fantasai
<TabAtkins> fantasai: the reason we didn't do it for style rules is we don't have OM ability to support that for style rules
<TabAtkins> fantasai: the expectation is that all decls would be in a .style property, and to do that we can't carea bout their order wrt .cssRules
<TabAtkins> fantasai: So worth pointing out that a .cssRules in a style rule will not contain the naked properties
<TabAtkins> fantasai: So that's different from in conditional rules, which is a little inconsistent.
<TabAtkins> fantasai: So do we really want that inconsistency, or do we want to do something like Lea's proposed magic, or...?
<fantasai> fantasai: but it is inconsistent
<fantasai> 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
<fantasai> TabAtkins: but we need to make sure that the cascade is consistently handled
<TabAtkins> fantasai: one thing we could do to keep it consistent is to have .style represent the first &-selector'd rule inside of .cssRules
<TabAtkins> fantasai: and then do that for both style and conditional rules
<Rossen_> ack fantasai
<TabAtkins> fantasai: And all subsequent groups of declarations would each get their own &-rule that would *not* be accessible via .style
<TabAtkins> fantasai: And that would give the same interface for both. It would keep the interleaved order of decls.
<fremy> (what fantasai just described is my preference)
<TabAtkins> fantasai: Not sure if that's what we want, but it would give consistent OM & cascading.
<fantasai> s/cascading/cascading while allowing preserving interleaved order/
<fantasai> plinss: [something]
<fantasai> TabAtkins: Yes
<TabAtkins> 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
<TabAtkins> plinss: so in `.foo { color: red; &{...} color: blue; }`, it's equivalent to `.foo { color: red; &{...} &{color: blue;}}`
<TabAtkins> emilio: perf-wise that seems slightly problematic
<Rossen_> q?
<TabAtkins> emilio: not quite clear how it would work with shorthands
<TabAtkins> emilio: I think coalescing them makes sense
<TabAtkins> emilio: otherwise using shorthands becomes different inside a conditional rule vs in a style rule
<TabAtkins> 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
<TabAtkins> Rossen_: Objections?
<TabAtkins> fantasai: Seems like there ar emore things to consider than we thought at the start, wouldn't be good to resolve yet
<TabAtkins> Rossen_: Okay, take the rest to the thread
tabatkins commentedon Jan 25, 2023
Summarizing the discussion:
& {...}rule"..foo { color: red; & { width: 100px; } width: 200px; }and.foo { color: red; @media all { width: 100px; } width: 200px; }should be consistent.width: 100pxwins in both examples above, orwidth: 200pxwins in both). (Currentlywidth: 100pxwins in nested style rules; the200pxgets pulled up to the front, and the nested rule is considered "after" it.)There were two branches of possible suggestions to address the above (particularly improving the OM consistency):
.styleaccessor to conditional rules, but somewhat magical - it just routes to the first child rule if it's a "default &{...} rule" as described above (and creates such a rule if one doesn't currently exist)..stylerather than a nested rule.)We ran out of time during the meeting to wrap up this discussion, but there was mild opposition to both of these ideas.
For (1), the thought was that this felt a little too magical, especially with the implicit rule creation+insertion on the first write to .style.
For (2), there was a minor perf concern - this would mean doing more selector matching in this case. There's also a concern about the interpretation wrt shorthands; shorthands + longhands are coalesced in particular ways when they're in the same style block, but would produce a slightly different behavior if spread across multiple style blocks.
I propose in our next take-up of this issue that we resolve to just do the "wrap naked properties in a
&{...}rule" part.cdoublev commentedon Feb 6, 2023
I guess you mean
.cssRulesinstead of.childRules.Should this implicit nested style rule always exist when the condition rule is nested, even if it has no declaration? I mean, does
CSSMediaRule.cssRules[0]have to be an empty nested style rule?If not, what should happen with
CSSMediaRule.cssText += 'color: red'? Should the previously exsting rule (& { color: green }) move to index1?tabatkins commentedon Feb 7, 2023
No, it only exists when it needs to exist.
Nothing should happen, because the spec defines
.cssTextas doing nothing when set. ^_^ (It's not technicallyreadonly, in the WebIDL sense, for historical reasons, but it's readonly in practice.)css-meeting-bot commentedon Apr 5, 2023
The CSS Working Group just discussed
[css-nesting-1] CSSOM for nested media query rules, and agreed to the following:RESOLVED: If naked property declarations inside @media become a thing, they are wrapped in a & {} rule.The full IRC log of that discussion
<fantasai> TabAtkins: last discussed in January<fantasai> ... basic question was, given that we allow nesting @media rule, and then you can put naked declarations inside, how do we expose that in the OM?
<fantasai> ... currently CSSMediaRule only exposes child rules
<fantasai> TabAtkins: The current proposal is if you have naked properties, they are effectively wrapped in a nested style rule with the & selector
<fantasai> ... and are exposed that way
<fantasai> ... This rule is the first rule in @media's child rules
<fantasai> ... and only exists if it needs to
<fantasai> TabAtkins: there were two further suggestions
<fantasai> ... one was to add .style accessor that roots to this first style rule
<fantasai> ... and/or to change parsing of all nested rules to wrap in & {} as well
<fantasai> TabAtkins: there was mild opposition to both ideas, but ran out of time
<fantasai> ... some concern about adding more rules to large stylesheet
<fantasai> TabAtkins: suggestion that all nested @rules that accept style rules usually, wrap in & {}
<fantasai> astearns: we could do things later?
<fantasai> TabAtkins: we couldn't do the second thing, but adding .style is still possible in the future
<fantasai> plinss: Some concern with ability to put raw declarations inside an @media rule that's nested
<fantasai> ... because we get yet another situation where you can mix declarations and rules, but defaulting differently
<fantasai> TabAtkins: ...
<fantasai> plinss: @media contains rules, so it's in rule error recovery
<fantasai> ... if you have stuff in side ...
<fantasai> TabAtkins: using same error recovery in all places
<fantasai> ... parser will fail rule parsing if it encounters ; before the block
<fantasai> ... today if trying to parse a rule, it will encounter the ;, stop there, throw it out, and then start parsing fresh
<fantasai> TabAtkins: semicolons have never been valid in the front of rules before, no expectation that this would break anything
<fantasai> plinss: if we have declaration top-level with braces, then something that follows braces?
<fantasai> TabAtkins: generally fine, some restrictions that we want to obey for future compatibility concerns, but very minor
<fantasai> ... part of larger issue
<fantasai> ... same effect here as anywhere else
<fantasai> ... won't affect current stuff, because nothing looks like that today
<fantasai> ... and in the future, you'll have the same set of restrictions everywhere, which are extremely minimal
<fantasai> ... but that's the larger parsing issue, same issue; only concern here is whether existing @media would be parsed differently, and I think the answer is "no in practice"
<fantasai> plinss: I accept it's part of the other issue
<fantasai> ... also concerned about older browsers parsing new content
<fantasai> TabAtkins: if you do nesting in an older browser, things will be very screwed up, misparsing is the least of your concerns
<fantasai> astearns: in the interest of moving forward, perhaps we can resovle to wrap things in an implicit & rule if we allow naked declarations inside an @media rule
<fantasai> plinss: I'm ok with that. I'm concerned about requiring &{} wrapping, ok if OM is same either way
<fantasai> RESOLVED: If naked property declarations inside @media become a thing, they are wrapped in a & {} rule.
<fantasai> emilio: Is this anonymous rule well-defined? it's at the beginning?
<fantasai> TabAtkins: details in the issue, always at the beginning if it's needed
<fantasai> plinss: If mix of declarations and rules, all the declarations sort to the beginning?
<fantasai> TabAtkins: yes
<fantasai> matthieudubet: if they actually write & { ... } explicitly, do we group with that?
<fantasai> TabAtkins: no
<fantasai> TabAtkins: if they explicitly write a rule, that's their rule, we don't do anything magic with that
<fantasai> matthieudubet: OK, that's not what Safari Tech Preview is doing right now
<fantasai> ... it tries to show the smallest representation with the same semantics
<fantasai> ... so it removes it
<heycam> @media (...) { & { color: red; } color: green; } /* element would be red */ ?
<fantasai> TabAtkins: This isn't in the spec yet, so it's not tested yet, so Safari will discover if it's doing the wrong thing
<fantasai> emilio: You can't just merge them because the order matters
<fantasai> heycam: So just want to clarify that all the bare declarations get hoisted to the top of the rule
<fantasai> ... so it might be a little confusing, about the order they'll applied
<fantasai> TabAtkins: it's the same behavior you get in ordinary nested style rules, though
<fantasai> ... all your naked declarations are sorted before all of the style rules
<fantasai> fremy: we resolved that we would be consistent, and maybe discuss what to do (but nobody filed an issue about that)
<fantasai> fremy: I think it's weird that they move, but should be consistent
<fantasai> plinss: if I have a bare red and then two wrapped blue, and then bare green, I expect it to be green and it will be blue
<fantasai> fremy: we discussed in the past, if we agree, we should file an issue about that
<fantasai> TabAtkins: this is a minor issue for compat purposes, so we can adjust this
<fantasai> TabAtkins: I think the answer is, don't put naked things after rules; but no reason to ban them
<fantasai> ... either way it's confusing
<fantasai> ... but I don't care which way we go
<fantasai> ... but aside from that, that would be a separate issue, I think we're done with this issue
<fantasai> astearns: resolution makes us consistent for the moment, but we can look at general issue on what to do with general declarations
<fantasai> ACTION: fremy or plinss to file issue about whether naked declarations get sorted to the top of the list or not
cdoublev commentedon Oct 13, 2023
Nit: can you please clarify serializing an implicit nested style rule?
https://drafts.csswg.org/css-nesting-1/#cssom
@media { & { color: red } })Bug 1797286 [wpt PR 36648] - [css-nesting] Serialize declarations in …