Open rachelandrew opened 5 years ago
This also comes up all the time when dealing with properties from SVG that are crossing over into CSS box layout & vice versa. For things like filter
we have had browsers that support one syntax for SVG elements only, and a different syntax for everything except SVG elements, and both syntaxes would pass an @supports
test.
(Aside: I really wish the spec for gap
had strongly encouraged browsers not to support the generic property name until they implemented support for the generic feature. I was rather disappointed when browsers rushed to ship the easy parser implementation of the synonym before addressing their layout code, for this reason. But the broader issue still remains.)
Perhaps the request should be that, in the absence of finer granularity queries, @supports
should return false
unless the implementation support exists in all usage contexts.
"Perhaps the request should be that, in the absence of finer granularity queries, @supports should return false unless the implementation support exists in all usage contexts."
That might be a bit more useful, however then there is a property and value that the author knows is supported, but seems to be returning false. At that point we are asking them to understand all possible contexts in which that property vould be used.
The fragmentation properties are probably better to look at here than gaps, which are likely to be implemented - say if we did a specific masonry layout or something.
At the moment a property like break-before: avoid
is specified for multicol, paged media, and regions. Assuming a wonderful world in which the property was supported in multicol and paged media, regions is a long way off, and probably won't happen in the current form. Which would mean that testing for break-* properties would be useless for the foreseeable future, as we don't have Regions so it can't be implemented in them. Most authors aren't even going to know about Regions and therefore why the property is returning false.
There's no good solution to this that doesn't break the fundamental value proposition of @supports
, which is that, since it's based purely on "does it parse", it requires no special table of return results in the browser, which history has shown us is destined to become some combination of stale and lies. I'm strongly against anything that tries to get "special-case" answers like this that aren't backed by a simple "try to parse it" mechanism.
(I definitely feel the pain, but it's not solveable here without making things overall worse.)
Is it feasible to have @supports
defined in the context of another selector? Something like:
body {
display: flex;
margin: 2rem;
@supports(column:gap) {
margin:0;
column: gap;
}
}
This way, the parsing stays simple and uses basic 'does it parse' logic.
Related...
Though @supports(position:sticky)
currently parses for all major vendors, Blink, which has the largest market share, still doesn't *fully support the value.
@SelenIT https://github.com/w3c/csswg-drafts/issues/2155#issuecomment-410978077
It seems that for some reason Chrome reverted their changes for fixing position:sticky behavior on
thead
element.
@tabatkins I understand your sentiment about making things worse, but currently the often most efficient ways to deal with false @support
scenarios are selector hacks, even if...
@dbaron https://github.com/w3c/csswg-drafts/issues/3051#issuecomment-416084354
Using hacks that assume support for X is equivalent to support for Y is a bad idea if those hacks are targeting current browsers. Such hacks are safe only if the features are implemented in all current browsers and the equivalence holds in the old versions where they're not implemented.
And the resolution in https://github.com/w3c/csswg-drafts/issues/3051 seems to express an opposite stance, getting rid of the ability to use some of these selector hacks.
Yup, selector hacks (or other browser-selection mechanisms) aren't great, but again, we already know that manually-maintained lists of support eventually rot, every single time it's ever been tried.
So it's a choice of somewhat encouraging bad individual-author behavior, or speccing bad UA behavior. I think the former does less damage overall.
Given, gap properties already exist for grid i.e. grid-gap
, grid-column-gap
, grid-row-gap
, aliases for gap properties could also be added for Flexbox. This would enable detection for gap support specifically in Flexbox using the existing Feature Query syntax. For example:
@supports (flex-gap: 10px) {
div {
display: flex;
gap: 10px;
}
}
@supports not (flex-gap: 10px) {
div {
display: flex;
margin: 10px;
}
}
I'm not sure however, how appropriate this approach would be regarding other properties.
It certainly seams that as there is greater cross-pollination between specifications, this kind of situation is likely to become more prevalent and a sustainable solution would be nice.
@simlawr That solution would somewhat cleanly solve the current issue regarding the -gap
properties, as authors can easily understand it.
Though I believe it doesn't scale because it means that for every new property that is shared between different contexts, there needs to be an alias introduced only for the purpose of feature detection. That burdens authors, as they have to remember the aliases when doing feature detection, implementors, as they have to implement the aliases only applying in certain contexts, spec. authors because they will have to remember to add those aliases when the property is used in different contexts, and documentation writers because they will also have to cover those aliases.
Unfortunately, I don't have a better solution for this. There is probably no automated way of doing this kind of feature detection.
Sebastian
I really like Rachel's original suggestion although I would change it to "with" rather than "in" because you are more looking for a generic relationship between the 2 rather than a parent/child relationship.
@supports (gap: 1px) with (display: flex) {
/* css code */
}
It doesn't sound that impossible for the computer to recognise.
The browser would need to check if both of the following conditions are true:
If not then it returns false
.
I am probably grossly simplifying the problem though. 😅
In the case of flex gap specifically, is there any workaround for web developers to detect support for it? It's only recently shipped in Chrome and even more recently landed in Safari Technology Preview, and without feature detection people will need to wait for another 3 years or so to be able to simply assume it will work.
In the case of flex gap specifically, is there any workaround for web developers to detect support for it?
Yes, there is but you have to use JS to detect it.
The following is based on this blog post: https://ishadeed.com/article/flexbox-gap/
// Set up a cache to prevent a DOM element from getting created for every use of the function
let isSupported;
function checkFlexGapSupport() {
// Use the cached value if it has been defined
if (isSupported !== undefined) {
return isSupported
}
// Create a flex container with row-gap set
const flex = document.createElement('div')
flex.style.display = 'flex'
flex.style.flexDirection = 'column'
flex.style.rowGap = '1px'
flex.style.position = 'absolute'
// Create two, elements inside it
flex.appendChild(document.createElement('div'))
flex.appendChild(document.createElement('div'))
// Append to the DOM (needed to obtain scrollHeight)
document.body.appendChild(flex)
// Flex container should be 1px high due to the row-gap
isSupported = flex.scrollHeight === 1
// Remove element from the DOM after you are done with it
flex.parentNode.removeChild(flex)
return isSupported
}
Thanks @Dan503! I guess what people then typically to do is set a class on the body element and use that instead of @supports
?
That used to be very common, don't know if it still is these days. Often people would use a tool like Modernizr to do that.
Is there any development on doing this either through an alias or an improvement to @supports? Using gap
in the context of Flexbox is virtually non-existent to me since it's not checkable.
I've stumble upon the above modified Modernizr check. For simple cases it might actually be easier to use a mixin through some CSS framework to "mask" gap so for instance calling a mixing gap(value)
to something like the over-simplified snippet below for demonstration purposes. After all, a fix for browsers still not supporting gap would be needed.
& > :not(:last-child) {
margin-right: value;
}
BUT you do have to account for Flexbox direction and wrapping, so something more like flex-layout(value, direction, wrap)
to make it more generic which can quickly make it a real pain vs using JS & DOM manipulation.
Resorting to either DOM manipulation or ignoring the feature I do hope there's some mediation without having to wait maybe another few years until gap is pretty safe. It seems following how @supports
works generics, such as gap
will simply need case-specific aliased names or they won't be able to be implemented correctly. It'd be quite unfortunate to see Modernizer forced to evolve into a bandaid of sorts.
@supports not (image-orientation: from-image) {
.flexbox {
margin: 0.5em auto;
}
}
@koller18 how's your code sample related to the subject? 🙄 It's not even a hack to "assume" parallel flexbox+gap support for any browser past + present.
I just had an idea for a solution to this: Custom @supports
rules.
Similar to script-based custom media queries, we could introduce custom @supports
rules. Those would allow to register some JavaScript worklet executing code like the one suggested by @Dan503.
The big advantage would be that authors could use @supports
in their CSS context. The only part in JS would be the check for the support. Another advantage would be the great flexibility allowing authors to create any kind of check, possibly even beyond CSS related things.
The downsides of this proposal are that executing that code can slow down CSS evaluation and worklets generally don't have access to the DOM. Though I'd say the slow-down in evaluation is neglectable because the script only has to be executed once when parsing the CSS. The bigger issue which I didn't give much thought yet is probably the DOM access. I assume the access probably needs to be restricted somehow to avoid performance and/or privacy problems.
Sebastian
To make my previous post a bit clearer, here an example for how I imagine this to work:
JS:
// gapInFlexboxSupport.js
class GapInFlexboxSupport {
name: '--gap-in-flexbox',
check: () => {
// Create a flex container with row-gap set
const flex = document.createElement('div');
flex.style.display = 'flex';
flex.style.flexDirection = 'column';
flex.style.rowGap = '1px';
// Create two, elements inside it
flex.append(document.createElement('div'), document.createElement('div'));
// Append to the DOM (needed to obtain scrollHeight)
document.body.append(flex);
// Flex container should be 1px high due to the row-gap
return flex.scrollHeight === 1;
}
}
registerSupportCheck(GapInFlexboxSupport);
// In document
CSS.supportsWorklet.addModule('gapInFlexboxSupport.js');
CSS:
@supports (--gap-in-flexbox) {
.flex {
display: flex;
gap: 8px;
}
}
Regarding performance and especially privacy concerns, we could create a worklet-specific document. (This is also what I expect in my example code.) So there is no relation to the actual document. Though this would, of course, also limit the possibilities for this feature.
Of course this also requires a way to check whether custom @supports
rules are supported, i.e. #6966.
Sebastian
In the case of flex gap specifically, is there any workaround for web developers to detect support for it? It's only recently shipped in Chrome and even more recently landed in Safari Technology Preview, and without feature detection people will need to wait for another 3 years or so to be able to simply assume it will work.
Much longer than this :/
2 years have past since this statement and at work we still do not allow gap
(in flex
elements) because we can not write fallbacks. We typically support 5-8 years of browser versions.
If @supports (flex-gap: 1px) {}
was available in the next version of each browser we could start using it.
We don't care that there are some versions with native support that would get the fallback. We do care about the browsers without native support.
That it is the 4th most searched for feature on caniuse shows how big a problem this is for stylesheet authors :/
Agenda+ to discuss my suggestion to add custom @supports
checks and also the other suggestions mentioned here.
Sebastian
I have some concerns with custom @supports
checks.
Needing a sub resource to check if a single property is usable in a single context can quickly escalate.
It requires authors to work around more issues instead of directly solving the issue at hand.
In my opinion this places too much burden on stylesheet authors.
All good questions! But they apply to all features that use worklets, e.g. several Houdini APIs, and the latter three also to external style sheets. And, as mentioned before, the proposed worklet would only need to run once. So at least in comparison to Houdini worklets which run many times consecutively it's cheap regarding performance.
Sebastian
For Houdini features I do not mind the tradeoff. The "user story" is straightforward here.
I want to do something browsers normally can't, so I reach for an API which enables custom behaviour.
Custom @supports
for native properties are a different case in my opinion.
It is not intuitive or obvious that stylesheet authors needs to create a supports worklet and think about the issues listed above when they actually want to use a standard property.
The same however is true for @supports (flex-gap: 1px)
or @supports (gap: 1px) in (display: flex)
.
There is no clear signal to stylesheet authors that @supports (gap: 1px)
will sometimes be true when they would expect it to be false.
Linters/build tools could warn when they encounter ambiguous @supports
rules to help stylesheet authors.
There is no clear signal to stylesheet authors that
@supports (gap: 1px)
will sometimes be true when they would expect it to be false.
Yes, authors still need to learn about this context relation and CSS itself cannot change this, unfortunately. So this issue focuses on a way to at least make it possible to detect context related support of features.
As @tabatkins pointed out earlier, user agent creators want to avoid a situation in which they have to maintain a table of context relations.
Custom @support
checks take away that burden from them and still provide authors with the possibility to check for context related support.
Linters/build tools could warn when they encounter ambiguous
@supports
rules to help stylesheet authors.
That's definitely true. And I am pretty sure that some of them already do that. But that's out of scope of the specifications, of course.
Sebastian
The CSS Working Group just discussed Testing support of partial implementations
.
I found inset
approximates support for flex gap pretty well. Compare browser comatibility data between the two:
https://developer.mozilla.org/en-US/docs/Web/CSS/gap#browser_compatibility
https://developer.mozilla.org/en-US/docs/Web/CSS/inset#browser_compatibility
@supports (inset: 0)
will not target any browsers which don't support flex gap. The @supports
check will also not target some browsers which do support flex gap: Chrome [84, 85, 86], Firefox [63, 64, 65], or Opera [70, 71, 72]. Support in WebKit matches perfectly.
If you're using mdn browser compat data to drop polyfills from your stylesheets it works well to avoid bundling clumsy margin-based polyfills.
Obviously I would love to see a fix for the underlying standards issue but this is a solution we can use today for the flex case.
With align-content
on block layout becoming available in browsers, this problem is getting some new life blown into it.
In https://css-irl.info/how-do-you-vertically-centre-an-element-in-css/, @michebarks wrote:
One thing that concerns me, is that this seems to fall into that tricky area where it becomes impossible to test for browser support and provide fallbacks using a feature query — much like gap when it was implemented for flexbox. As align-content is well-supported for Grid and flexbox, the feature query doesn’t help us here.
@supports (align-content: center) { /* This will resolve true for any browsers supporting the property in grid or flexbox */ }
It would be great to see some improvements to how feature queries can handle these sorts of situations.
I was recently experimenting with some unrelated things, where I used inline-blocks for a layout, and I really missed the absence of a gap
in the normal flow. If we did have an ability to detect partial implementations like this, it could be more likely that we could get it (“normal flow gap”), as well as other similar cases (extending some very specific property to work in different contexts).
The absence of the ability to easily detect the flex
+ gap
support is also a reason why our design system currently has a legacy workaround for the flex “gap” using margins: it is not trivial to migrate to native gap
.
I really like the idea of using something like (gap: 1px) with (display: flex)
as a way to test how multiple properties & their values interact.
Though I can see the challenge in how that could be defined, and for authors it might be confusing trying to understand which pairs of properties they could use in @supports
like that.
We don't have many places like this outside of gap
, so I think having a “hardcoded” syntax like @supports (gap: 1px !flex)
could work. Maybe not exactly like this, but the idea is for any properties+value pairs that get changes in the specs/implementations that require different type of support detection, to have an explicitly defined “supports flag” which could be used only as a part of @supports
.
I do think this case is exceptional enough that not coming up with an abstract “ideal” solution is worth it here, and in this case, it is better to get a working solution sooner rather than later.
I think @tabatkins's https://github.com/w3c/csswg-drafts/issues/3559#issuecomment-458734231 (which I agree with) was a good explanation of why we don't want a general solution here.
However, I think it might be reasonable to add specific, one-off solutions when we think they're important enough. They'd need to be for significant enough features that we'd be willing to add an extra keyword to CSS for them, and that an implementor wouldn't miss that it needed to be implemented, and that we'd be willing to write specific web-platform-tests to verify and monitor the results closely to make sure the rollout doesn't go wrong. And we could probably choose reasonably verbose keywords.
For example, for the two examples discussed recently we could add something like gap-on-display-flex
or align-justify-properties-on-blocks
keywords so that it would be possible to write:
@supports feature(gap-on-display-flex) {
/* ... styles ... */
}
(It's likely too late for the former to be useful at this point, but the latter is something we really could consider now.)
I'm not too happy that we can't find a general solution for that, though I do like this pragmatic approach. This allows us to make progress on this topic and we do have to make fast progress to avoid repeating the same mistake as with gap
in Flexbox.
As implementation of align-content
for block containers has begun on Gecko and Blink and seems to already be finished in WebKit (@fantasai Is it going to ship, already?), we should decide on this soon and push implementations to ship detection for it along with the feature.
Sebastian
Even if we're going to do one-off keywords, I think we need a consistent syntax for property-X-applies-to-display-type-Y.
The CSS Working Group just discussed [css-conditional] testing support of properties and values with partial implementations
, and agreed to the following:
RESOLVED: adopt keyword based feature queries, with names to be bikeshedded later, with the expectation that we will use them rarely and test and message their addition carefully
RESOLVED: Add a keyword for alignment on blocks, with the specific name TBD
Even if some browsers already support gap in flexbox without this feature detection, it would still be great to add a keyword for it. As @romainmenke pointed out earlier, making existing versions of browser apply the fallback implementation with margins instead of using gap would often be acceptable, while still using gap for newest browsers and supporting a fallback for browsers without support for gap.
so I did not follow all the threads surrounding this or have read all minutes... I would be a user of this feature.
FWIW a syntax like @supports feature(gap-on-display-flex)
seem very counter intuitive and artificial in nature.
It requires learning a (albeit limited) vocabulary to describe a problem, requires maintenance (in code and documentation) and specific documentation (lists) that would be hard to "guess" where to find. And whomever writes a blog post would also need to mention these magic keywords and all existing documentation on either the main or supplemental feature would be outdated.
Some generic feature test on the other hand like @supports (gap: 1px) with (display: flex)
seems more intuitive to me as it describes spot on the query of a problem that needs solving: does this work in the context of that?
Thank you folks for your great work! Have a nice time.
Some generic feature test on the other hand like
@supports (gap: 1px) with (display: flex)
seems more intuitive to me as it describes spot on the query of a problem that needs solving: does this work in the context of that?
I think the basic reason not to do this is that there are probably a few thousand different options for the things on either side of the with
in your proposal (different CSS properties, plus different generations of values added to those properties over time), which means there are a few million possible with
combinations. There's no ready source of data for the answer to those few millions of questions, so we (both the working group and implementors) would need to sit down and decide what the correct answer to each one is, ensure engines implement them correctly, and test them.
If you combine that with the reality that there's a single digit number of these questions that we know are much more interesting than all the others, it seems much more solvable to deal with the single digit number of questions than with a few million questions.
@dbaron Thanks for your reply! I believe this makes sense :-)
My thinking was: the CSS engine should-would eventually know anyway if any property is "valid" within a specific context as it has to render it at some point and decide if it knows what it's about. And we can already have a gazillion warnings abourt invalid / unsupported properties in the console -- like vendor prefixed stuff. But maybe this "path" is too expensive to follow in the initial parser level.
Have a nice time.
Is there a reason the previous suggestions list the display type last? It seems more intuitive to me to have keywords that list the display type first and then follow it with the requested feature.
feature(flex-gap)
Using feature()
is not my favorite solution.
Since the current resolution is to implement keywords only on rare occasions (1 a year, per the log) and to make it easy to educate everyone to use with minimal confusion, I think it would be better to use a keyword that screams "this is a very special use case" rather than a generic term like feature()
. I think using feature()
sets a reasonable expectation that you can check for any set of CSS features, and it will cause confusion when it doesn't work that way.
Even something like case()
or keyword()
or just key()
seems better to me (though I don't love any of those).
I'm concerned that one-off keywords will greatly increase the burden on the author's memory.
I have a @feature
proposal, This is flexible enough to essentially do what JS currently does to create empty elements for detection. But I am not sure it's feasible.
@feature --align-content-on-block {
display: block;
align-content: center;
}
@supports --align-content-on-block {
.foo { /* do something */ }
}
@feature --detecting-at-rule {
@container style(--foo: true) {}
}
I think @yisibl's proposal in https://github.com/w3c/csswg-drafts/issues/3559#issuecomment-2138955279 has the issues described in https://github.com/w3c/csswg-drafts/issues/3559#issuecomment-1915148442 .
There are a few places where
@supports
is unable to help authors as the property and value they want to test for is supported by the user agent, but not in the context they want to use it.The obvious one (and where it keeps coming up at the moment) is testing support of any of the gap properties
column-gap
,row-gap
orgap
. I can test for support ofcolumn-gap
and every browser will tell me it has support, however if I then try to use it in flexbox, I will only get a gap in Firefox, as that is the only user agent to have an implementation right now.This means authors can't create a nice fallback with margins, then removing the margin on testing for support. It is likely that other box alignment properties - perhaps as browsers start to implement them for block layout, will have the same issue.
Other places this might be an issue are for fragmentation, I might want to test if fragmentation properties are available in a certain context.
It feels as if we need a way to ask for support of, for example,
column: gap
indisplay: flex
. I don't know if that is even feasible in implementations, but it seems that this is likely to continue to be an issue as specs become more modular.I found an older issue which also refers to partial implementations not being testable: https://github.com/w3c/csswg-drafts/issues/1167