Open SebastianZ opened 1 year ago
Maybe the simplest solution would be to reuse the selector()
function for that.
So this could detect whether nesting style rules is supported:
@supports selector(& .foo) {
…
}
Though that only covers style rules. Support for nesting other rules like media queries might require a different approach. One solution would be to support testing of complete rules, as I suggested earlier. An example of that could look like this:
@supports (.foo { @media (width >= 500px) { } }) {
…
}
Another, more flexible but also more complex approach would be script based queries, which I also suggested earlier.
Sebastian
I highly doubt authors will use something like
@supports selector(& .foo) {
.wrapper {
color: magenta;
& .foo { color: cyan; }
}
}
@supports not selector(& .foo) {
.wrapper { color: magenta; }
.wrapper .foo { color: cyan }
}
They will just use the good ol
.wrapper { color: magenta; }
.wrapper .foo { color: cyan }
The only reasonable fallback strategy seems using JS to read the raw contents of the <style>
, parse it manually, and if there are nested rules, desugar them. But detecting support for nesting in JS is already possible:
function supportsCSSNesting() {
try {
document.querySelector("&");
return true;
} catch (e) {
return false;
}
}
Support for nesting other rules like media queries is also posssible:
var style = document.createElement("style");
style.textContent = ".foo { @media (width >= 500px) { } }";
document.head.append(style);
var {sheet} = style;
style.remove();
return sheet.cssRules[0]?.cssRules?.length === 1;
We may want to add nicer ways, but I don't think it's a must.
I agree that authors will likely not write single stylesheets with both nested and un-nested versions of selectors. They'll either, as you say, write the stylesheet without nesting until they no longer care about older browsers, or use a preprocessor and let it emit the un-nested version.
However, what's reasonable, and should be encouraged, would be:
@import "nested-and-small.css" supports(selector(&));
@import "not-nested-and-large.css" not supports(selector(&));
(or ideally do the equivalent in <link> tags)
This way the author can use a preprocessor to generate the larger, un-nested stylesheet, but the user of a newer browser doesn't have to pay the download cost.
While nesting should be detectable via script, we need to be able to feature detect it without script (or UA sniffing) as well.
@plinss I totally agree with what you say and also thought of the usage in @import
or the HTML variant for <link>
elements. I guess I should have mentioned that.
@Loirooriol Relying on JS to solve this is also one of my ideas from above, though ideally JS shouldn't be a requirement for feature detection.
If testing for selector(&)
is already valid, then we at least have detection support for nesting style rules.
Though being able to test for other nesting features is still important. So there's no gap between implementations and feature detectability.
Sebastian
@import "nested-and-small.css" supports(selector(&));
@import "not-nested-and-large.css" not supports(selector(&));
No browser has implemented this (yet?)
No browser has implemented this (yet?)
Sure, but no browser has really implemented nesting yet either (STP and Chrome experiments not withstanding). The WG hasn't resolved on the actual mechanism to feature detect nesting yet either, the above was a (reasonable) placeholder. Given there are still open questions on the final syntax of nesting it doesn't make sense to finalize the feature detection yet.
I expect that the WG will finalize how to feature detect nesting when we finalize nesting itself, and any browser implementing nesting will implement the feature detection at the same time (it should be part of a compliant implementation).
@plinss In case you are not aware of it, Blink plans to ship the "experiment" in version 112. https://groups.google.com/a/chromium.org/g/blink-dev/c/eFCrkiLynfU/m/JLsh3zQuAAAJ
However, what's reasonable, and should be encouraged, would be:
This way the author can use a preprocessor to generate the larger, un-nested stylesheet, but the user of a newer browser doesn't have to pay the download cost.
This is a tangent and I don't want to derail this thread but @imports ".." not supports(selector(&))
doesn't help browsers without nesting support. Even if supports
queries for @import
ship simultaneously with nesting. Any @imports
with a supports
condition will be downloaded but never applied.
In theory it is the best method, but because it never shipped it isn't practically useful.
@plinss In case you are not aware of it, Blink plans to ship the "experiment" in version 112.
I'm aware, unfortunately just another example of Google (potentially) being Google and deciding that they know best without allowing the standards process to come to a determination of a mutually agreed best path forward. Then leveraging their market dominance to try to establish a de-facto standard. (And Apple isn't much better in their messaging around STP).
@plinss I would like to ask you to revisit this comment. We are here, participating in the standards process.
The web standards process is not now, nor has it ever been, a gate that controls features that are shipped in implementations. At the same time, as a large engine implementer, we have a responsibility to participate in good faith to build consensus on features; I believe we are doing that here. If you disagree, as per our policy (https://github.com/cwilso/standards-of-behavior), I'm happy to discuss in more depth.
@cwilso I edited my comment to remove the phrase that you found offensive. However, while I agree that Google is here, and is participating, I fail to see how shipping a feature in a form that is still under contention and discussion can be characterized as "participating in good faith". You are of course free to ship whatever you want, but you don't get to characterize paving your own path as a "standards process". Your Blink dev thread makes it clear that your shipping decision process isn't waiting for this issue to be resolved by the WG, let alone an FO.
I guess what we can agree on is that browser vendors are eager to release these nesting capabilities. So they should also be interested in feature detectability, otherwise the adoption of this big new feature will suffer.
Sebastian
@romainmenke Very good point! So it seems that we need a different approach so that UAs with nesting support can block loading stylesheets and rules without nesting to avoid loading them twice in that case.
Sebastian
I’ve tested several approaches in the past:
selector(&)
supportThe caveat with approach 2 is that it assumes a browser does not ship &
as a selector without shipping nesting. IIRC @tabatkins mentioned somewhere (can’t seem to find back where) that &
should not ship separately, so it’s safe to rely on it.
In practice, though, I don’t think authors will test nesting, as they’ll simply see the nested styles not get applied in that case:
For @supports selector(&) {}
to be useful it is also critical that browsers ship support for the nesting selector at the root at the same time as actual nesting : https://github.com/w3c/csswg-drafts/issues/5745#issuecomment-1302487789
I've created a pull request with a basic WPT test : https://github.com/web-platform-tests/wpt/pull/38373
Currently Chrome and Safari TP do not support &
at the root.
If they were to ship today there would be no way to detect support for &
at the root in the future.
Edit : there was already a test added recently : https://wpt.fyi/results/css/css-nesting/top-level-is-scope.html?label=master&label=experimental&aligned&view=subtest&q=nesting
@Loirooriol
Chrome(e.g. 109) returns null
for document.querySelector("&")
in browsers that don't support CSS nesting.
So, the detection functions need a little adjustment:
function supportsCSSNesting() {
try {
const s = document.querySelector("&");
if (!s) return false; // For Chrome 109/110
return true;
} catch (e) {
return false;
}
}
Unfortunately, @supports(selector(&))
and CSS.supports('selector(&)')
always returns true.
Edit : there was already a test added recently : https://wpt.fyi/results/css/css-nesting/top-level-is-scope.html?label=master&label=experimental&aligned&view=subtest&q=nesting
Even before this, I believe @supports selector(&)
should have worked? The selector was defined to exist and parse just fine, just not to match anything at the root. (Now it's defined to something else, of course, but that shouldn't affect @supports
.)
Even before this, I believe
@supports selector(&)
should have worked?
It does indeed work, but the question here is if @supports selector(&)
can be used as proxy for testing actual nesting support.
This approach is only viable if browsers ship nesting and "&
at the root" at the same time because there is no way to differentiate between them.
It does indeed work, but the question here is if
@supports selector(&)
can be used as proxy for testing actual nesting support.
See comment above:
IIRC @tabatkins mentioned somewhere (can’t seem to find back where) that
&
should not ship separately, so it’s safe to rely on it.
It is the obvious/logical thing to do, but maybe it can also be enforced from within the spec?
I believe
@supports selector(&)
should have worked?
&
is part of nesting, so the check should have been false with CSSNesting
disabled.
It is the obvious/logical thing to do, but maybe it can also be enforced from within the spec?
Seems too late, Blink shipped &
in https://chromiumdash.appspot.com/commits?commit=336be2dad52bf3454d6063bf36ea4e49a3a74aa7
Yeah, I assumed we wouldn't ship the nesting selector separate from the nesting feature, as it was useless on its own and provided a nice benefit to feature detection. Ah well.
Blink shipped & together with nesting support. The commit you're linking to (which did indeed add nesting and & at the same time, but the version that was current at the time) was all behind an experimental flag; it would not do anything in a normal, stable browser.
I bisected without experimental flags and I got that commit. And effectively with the stable 109.0.5414.119 snap I can see that &
is considered a valid selector even if nested rules are disabled.
That definitely wasn't the intention of the code, but OK, it's impossible to back and change now.
That was fixed in https://bugs.chromium.org/p/chromium/issues/detail?id=1414012 and backport requested to Chrome 111. (Thanks @sesse!)
That was fixed in https://bugs.chromium.org/p/chromium/issues/detail?id=1414012 and backport requested to Chrome 111. (Thanks @sesse!)
But impossible to backport to 109 and 110 right?
So @supports selector (&) {}
is no longer usable as a reliable detection?
Correct, we can't fix existing versions in the wild. (Or rather, we reserve that sort of thing for OMG THE WORLD IS ON FIRE security issues.)
But Chrome versions decay in usage quickly.
So
@supports selector (&) {}
is no longer usable as a reliable detection?
(Cross-posting from Chromium’s I2S)
Authors can work around Chrome 109+110 incorrectly reporting &
support by also testing for a feature that ships in any release after Chrome 110. A good candidate would be cos()
, which ships in Chrome 111:
@supports selector(&) and (scale: cos(90deg)) {
…
}
This check excludes Chrome 109+110, but includes Chrome 111 with the feature flag on.
More importantly this extended check won’t exclude Safari or Firefox, as they too support cos()
– from, respectively, versions 15.4 and 108 onwards according to caniuse.com.
To be honest, feature detection here is less likely to affect users migrating to native nesting, instead, I think the more fatal problem is the desugaring to :is()
.
To be honest, feature detection here is less likely to affect users migrating to native nesting, instead, I think the more fatal problem is the desugaring to
:is()
.
This was revisited here : https://github.com/w3c/csswg-drafts/issues/8310 But it isn't related to and doesn't affect feature detection for nesting.
I agree that authors will likely not write single stylesheets with both nested and un-nested versions of selectors. They'll either, as you say, write the stylesheet without nesting until they no longer care about older browsers, or use a preprocessor and let it emit the un-nested version.
However, what's reasonable, and should be encouraged, would be:
@import "nested-and-small.css" supports(selector(&)); @import "not-nested-and-large.css" not supports(selector(&));
(or ideally do the equivalent in tags)
This way the author can use a preprocessor to generate the larger, un-nested stylesheet, but the user of a newer browser doesn't have to pay the download cost.
While nesting should be detectable via script, we need to be able to feature detect it without script (or UA sniffing) as well.
I suspect once Nesting has critical mass, what will be more common would be this pattern:
HTML:
<link rel=stylesheet href="nested.css">
nested.css:
@import "compiled-to-nonnested.css" not supports(selector(&));
/* nested CSS */
@import "compiled-to-nonnested.css" not supports(selector(&));
This doesn't work, it was never implemented. So there is no advantage to it.
see : https://github.com/w3c/csswg-drafts/issues/8399#issuecomment-1418273286
There is also no way to detect support for the even more relaxed syntax where selectors can start with an identifier.
In Chrome 113 :
CSS.supports('selector(& bar)')
// true
CSS.supports('selector(bar &)')
// true
Safari TP doesn't seem to work at all :
CSS.supports('selector(&)')
// false
CSS.supports('selector(& bar)')
// false
CSS.supports('selector(bar &)')
// false
This seems problematic if the plan is to relax the syntax somewhere in the future.
# Safari TP doesn't seem to work at all :
CSS.supports('selector(&)') // false
Hmm, that looks like a bug to me, given Safari TP does support that selector. Searching the WebKit bug tracker, I found this bug filed for it: https://bugs.webkit.org/show_bug.cgi?id=252301
# There is also no way to detect support for the even more relaxed syntax where selectors can start with an identifier.
If one could nest inside of selector()
, it would be possible:
CSS.supports('selector(div { span })');
I think this shortcoming should be plugged.
Chrome is shipping nesting in ±2 weeks. This issue seems important to give some extra thought before then.
Especially : https://github.com/w3c/csswg-drafts/issues/8399#issuecomment-1445128344
There is also no way to detect support for the even more relaxed syntax where selectors can start with an identifier.
The CSS Working Group just discussed [css-conditional-5][css-nesting-1] Feature detection for nesting
.
plinss: I believe it's very important that we have a stable and reliable mechanism before we ship anything, can we agree on that TabAtkins: I think it's a good idea, I'm not going to commit to it plinss: I think shipping this whithout reliable feature detection is a huge mistake
I totally agree with @plinss here. I fear, Google's strategy for shipping nesting without proper support for feature detection will have negative effects. Without a proper mechanism for feature detection, nesting is doomed to either not being adopted or to cause pages to break.
As @romainmenke pointed out earlier, the main issue is that browsers never implemented @import "..." supports();
. Without that, authors don't have a global switch for browsers with nesting support and those without. And nesting support can only be checked when wrapping specific rules in @supports selector(&) { ... }
within a stylesheet.
Unfortunately, using @supports
in this case mainly means unnecessarily bloating the stylesheet because authors have to provide the same rules twice for browsers with and without support. I.e. they have to wrap every nested rule in @supports selector(&) { ... }
. And for browsers that don't support nesting they need to add another @supports not selector(&) { ... }
section basically duplicating their rules.
@bramus
In practice, though, I don’t think authors will test nesting, as they’ll simply see the nested styles not get applied in that case:
* Build a base style layer that doesn’t use nesting * Layer nested styles on top
As outlined above, layering nested styles on top of not nested ones is not really a use case. Authors normally want to apply the same rules independently of how they are written.
Coming back to the issue outlined by @romainmenke, I believe, the main issue is allowing nesting while being backwards compatible and avoid putting the burden onto users. That means, we need a way for UAs that support nesting to somehow suppress importing external stylesheets. Only when that's possible, nesting can really be used by authors without disadvantages.
Sebastian
# The CSS Working Group just discussed
[css-conditional-5][css-nesting-1] Feature detection for nesting
. … < heycam> @supports rule(p { & { } }) < fremy> @supports (&{}) < bradk> @supports (nesting) { … }
I like @heycam’s suggestion here to be able to pass an entire rule-set into @supports
. Of the three suggestions, it has the widest coverage.
This would help not only here with nesting, but also cover future/other CSS features such as @scope
, as authors can pass in @scope (.foo) to (.bar) { & { color: hotpink; } }
to detect if that specific syntax is supported or not. Using a keyword like nesting
doesn’t cover that.
This extension to @supports
would pair really nicely with @import … supports(…)
(suggested by @plinss above) and link[media="supports()"]
(something that was discussed for Cascade Layers).
This extension to @supports
doesn’t seem mandatory to be able to detect nesting though, as passing selector(&)
will cover “is nesting supported?” just fine as the reality is that nesting and &
ship together. Yes, some pre-release versions of Chrome/Safari incorrectly claim (or don’t claim) support for &
, but given that these are pre-releases or versions that require the user to enable a feature flag, I think it’s safe to assume that users of those versions update their browsers very often.
Yes, some pre-release versions of Chrome/Safari incorrectly claim (or don’t claim) support for &, but given that these are pre-releases or versions that require the user to enable a feature flag, I think it’s safe to assume that users of those versions update their browsers very often.
It wasn't a pre-release version of Chrome that incorrectly claimed to support &
:)
There also isn't a way to differentiate between what shipped in Chrome 112 and the highly likely direction of the specification :
// In Chrome 112
CSS.supports('selector(&)')
// true
CSS.supports('selector(& div)')
// true
CSS.supports('selector(div &)')
// true
Ideally authors have a reliable way to detect support for the final version of nesting, even in older versions of Chrome.
It wasn't a pre-release version of Chrome that incorrectly claimed to support
&
:) True. There’s a workaround – which covers all browsers – I mentioned before, but admittedly it’s a nasty hack.There also isn't a way to differentiate between what shipped in Chrome 112 and the highly likely direction of the specification
If more robust/reliable feature detection through < method-this-issue-settles-on > lands, Chrome 112 wouldn’t support that new detection method, so it would still get the non-nested styles because it has a more limited nesting support.
Rewinding a bit. Say the suggested @import … supports(…);
lands. Currently using those statements in any browser does not load any of the referenced CSS files, as they don’t understand the statements.
@import "nested-and-small.css" supports(<method-to-detect-nesting>);
@import "not-nested-and-large.css" not supports(<method-to-detect-nesting>);
That means, the snippet above won’t cut things here:
@import … supports(…);
+ nesting will load nested-and-small.css
.@import … supports(…);
will load none of those files.What is parseable is, is this … but then you’d end up with double loads + competing stylesheets in browsers that do support @import … supports(…);
@import "not-nested-and-large.css";
@import "nested-and-small.css" supports(<method-to-detect-nesting>);
Not sure how this could be solved in a nice way 🤔
Correct, although browsers do download all stylesheets, even when they ignore them. Bandwidth usage is roughly doubled for end users and they still have a broken page.
Some browsers maybe don't intend to ship nesting support in the sort term.
These could add support for @import … supports(…);
. This makes it possible to have a transition period where some browsers have nesting support "now" and others only "later".
It doesn't help all users of older browser versions but it might still be a useful tool.
The CSS Working Group just discussed [css-conditional-5][css-nesting-1] Feature detection for nesting
.
Summarizing the remaining parts:
The first poing still directly refers to this issue. For the second point I can create a separate issue.
Sebastian
We need to provide a smooth way to transition to nesting, so authors can detect whether nesting is supported by the user agent. So they can provide fallback solutions for UAs that don't support it.
There where already some prior discussions related to this topic, though no explicit issue or resolution regarding this, as far as I can tell.
The major discussion related to this is #8349, but its focus is rather on error recovery than feature detection. Another slightly related one is #2463, though it rather features detection of at-rules and their descriptors.
So I opened this issue to explicitly discuss how nesting support can be made detectable. I believe this is a must for any implementation for shipping nesting capabilities to reach a high level of adoption and avoid page breakages.
Sebastian