Unsupportable Promises
Published 11 years, 9 months pastOver the past year and a half, the CSS Working Group has been working on a CSS Conditional Rules Module Level 3 module. Now, don’t get overexcited: this is not a proposal to add generalized, formal if/then/else or switch statements to CSS — though in a very limited way, it does just that. This is the home of the @media
rule, which lets you create if/then conditions with regard to the media environment. It’s also the home of the @supports
rule, which lets you…well, that’s actually more complicated than you might think.
I mean, what do you think @supports
means? Take a moment to formulate a one-line definition of your understanding of what it does, before moving on to the rest of this piece.
If you’ve never heard of it before and wonder how it works, here’s a very basic example:
body {background-color: white;}
@supports (background-color: cornflowerblue) {
body {background-color: cornflowerblue;}
}
The idea is that if the browser supports that property:value combination, then it will apply the rule or rules found inside the curly brackets. In this sense, it’s just like @media
rules: if the conditions in the parentheses are deemed to apply, then the rules inside the declaration block are used. The module refers to this ability as “feature queries”.
There are some logical combination keywords available: and
, or
, and not
. So you can say things like:
body {color: #222; background-color: white;}
@supports ((background-color: cornflowerblue) and (color: rgba(0,0,0,0.5))) {
body {background-color: cornflowerblue; color: rgba(0,0,0,0.5);}
}
Okay, but what does that actually mean? Here’s what the specification says:
A CSS processor is considered to support a declaration (consisting of a property and value) if it accepts that declaration (rather than discarding it as a parse error). If a processor does not implement, with a usable level of support, the value given, then it must not accept the declaration or claim support for it.
So in that first sentence, what we’re told is that “support” means “accepts [a] declaration” and doesn’t drop it on the floor as something it doesn’t recognize. In other words, if a browser parses a property:value pair, then that qualifies as “support” for said pair. Note that this sentence says nothing about what happens after parsing. According to this, a browser could have a completely botched, partial, and generally unusable implementation of the property:value pair, but the act of recognizing means that there’s “support”.
But wait! That second sentence adds an additional constraint, after a fashion: there must be “a usable level of support”, the lack of which means that a browser “must not…claim support”. So not only must a browser parse a property:value pair, but support it to “a usable level”.
But what constitutes a “usable level”? According to everyone who’s told me that I was wrong about vendor prefixes, any browser implementation of a feature should be complete and error-free. Is that what’s required to be regarded as a usable level? How about if the implementation has one known bug? Three? Ten? Can any of them be severe bugs? What about merely serious bugs? What if two browsers claim usable support, and yet are not interoperable?
So. How does the definition of @supports
match the one-line definition I asked you to formulate, back at the beginning? Are they exactly the same, or is there a difference?
I suspect that most people, especially those coming across @supports
for the first time, will assume that the word means that a browser has complete, error-free support. That’s the implicit promise. Very few people think of “supports” as a synonym for “recognizes” (let alone “parses”). There’s a difference, sometimes a very large one, between recognizing a thing and supporting it. I’m sure that browser teams will do their best to avoid situations where a property:value pair is parsed but not well supported, but it’s only a matter of time before a “supported” pair proves to be badly flawed, or retroactively made wrong by specification changes. Assuming that such things will be allowed, in an environment where @supports
exists.
If feature queries were set with @feature
, as media queries are set using @media
, or even if the name were something along the lines of @parses
or @recognizes
, I’d be a lot less bothered. The implicit promise would be quite a bit different. What I feel like we face here is the exact inversion of vendor prefixes: instead of a marker for possible instability and a warning that preserves the possibility of changing the specification when needed, this pretends to promise stability and safety while restricting the WG’s ability to make changes, however necessary. My instinct is that @supports
will end up in the same place: abused, broken, and eventually reviled — except this time, there will be the extra bitterness of authors feeling that they were betrayed.
Comments (34)
Yuck. Nothing like writing the same CSS statement multiple times.
You know I’m totally with you on the
@supports
skepticism, but I just had to say that@parses
/@recognizes
strike me as highly cool ideas.As far as I can see this has no advantages whatsoever over Modernizr and Sass’s ampersand selector:
.rgba
. It looks absolutely hideous, to boot. Why aren’t the support checks in shorthand?Pingback ::
Bruce Lawson’s personal site : Reading List
[…] Unsupportable Promises – Eric Meyer gets all negged out and totally heavy about @supports in CSS […]
I would love to have this. It will cut out a lot of heavy JavaScript and DOM manipulation!
First things first: “me too!”
If such a thing is implemented, I figure the syntax needs changing so that the rules inside the block are applied if possible, the actual conditional being applied by the rule itself. The duplication is, put briefly, a drag.
Meanwhile, the existing SOP – following the vendor-prefixed pair with the standards-compliant pair – by comparison has a lot to recommend it in terms of workflow, in spite of the potential it creates for legacy-bloating.
…And as you imply, the definition of “usable” needs to be changed to “complies fully with the Recommendation at Level 3 or higher.” Those of us who actually had to contend with “DOM Level 0” remember all too well what “usable” means in the wild.
On balance, I’d rather run the risk of turning @supports instead of using selector hacks and conditional blocks… but this only works for as long as the vendors continue to evince a cooperative spirit.
Pingback ::
Unsupportable Promises | Darwin
[…] Unstoppable Promises » Unstoppable Promises The @supports rule recently added to the CSS Conditional Rules Module and landed in WebKit stands to be a powerful tool in a web developer’s belt, but there are some major doubts. UAs aren’t known for claiming “support” only when they truly support a feature in a fully interoperable and bug-free way. Could this potentially exciting addition to CSS be doomed from the outset? vía Darwin Otoniel http://meyerweb.com/eric/thoughts/2013/03/19/unsupportable-promises/ […]
How would this differ from Modernizr which most people already use? Would Modernizr’s own detection methods be anymore accurate? I recall a Tweet by Paul Irish saying that Modernizr now uses @support to feature test properties.
I agree that browser vendors have the potential to be somewhat untruthful with how they report support. (I recall an old Blackberry device claiming to support touch events when it in fact didn’t awhile back while testing)
People already (rightly) blast browsers when they parse something before they actually support it. Chrome has done that several times and gotten reamed, so we’ve gotten more conservative in exposing parsing code until the feature is reasonably ready. Other browsers are similar, so the parsing test from @supports should actually be reasonably useful.
I mean, come on, this kind of thing has been done before, and it’s universally failed. If it fails again, no big loss, we’ll just have another useless feature hanging off the edge of the platform. But we believe we’ve come up with the best attempt so far to get browsers not to lie, or at least not to lie *egregriously*. You can’t do any better with browser-declared support – you need to do your own feature testing otherwise.
Your idea at the end of an @feature rule, though, is *exactly* the kind of thing that has already failed (the hasFeature() function), and for reasons that were obvious-in-hindsight – if the return value of the query is completely separate from the actual implementation of the feature, just querying a table in the code with feature names and booleans, then it’s easy, *trivial* even, to lie. You don’t even have to be doing evil to lie; you just have to misunderstand what the boolean is exactly supposed to represent, so you automatically set it to true as soon as you start working on a feature.
At least with @supports the return value and the implementation are tied together, as much as we can possibly achieve without just embedding a JS feature test inline. I think there’s good reason to believe this will be truthful more often than any previous attempt at this kind of thing.
Note, though, that you don’t even have to wait for a nebulous future feature before you hit a failure point. If you grabbed a mobile device today that supports @supports (do they exist yet?), and asked it for position:fixed support, it would say yes with a straight face, despite the fact that position:fixed on mobile acts dramatically different than on desktop, and is often totally crazy.
This isn’t a panacea. It won’t be perfect. But hopefully it will be useful.
@supports is not Modernizr. Modernizr is a Javascript library, and thus, requires Javascript. This accomplishes feature detection without requiring Javascript to be enabled.
For the past few years, Modernizr has detected support for all sorts of CSS features primarily by seeing if they are successfully parsed. So as Sylvain Galineau (previously IE team, now Adobe) pointed out on twitter, @supports is hardly any different to the feature detection techniques that we’ve used thus far.
It’d probably be useful to look at some real-world examples here:
* Opera supported linear gradients before they did radial gradients. So “cssgradients” in Modernizr gave half the story there. @supports was designed with this in mind. Because the value is required, you can’t just say “do you support all of text-overflow?” You must ask for text-overflow:ellipsis support specifically.
* blackberry 6/7 don’t support angles in their css gradients. In @support’s case, this can be mitigated.
* CSS Regions: All the feature flags done in WebKit enable not just the rendering of experimental CSS stuff, but these “ifdefs” are around the parsing too.
—
As a side note, I don’t understand the antagonism I’m seeing towards @supports. This is the first time we’ve had any real matter of recourse for a browser that exposes a false positive. Maybe this is just something I’m more used to than most, based on developing Modernizr for the past few years, but false positives happen (and that’s what you’re worried about here, obviously).
When FPs have happened before, I can report them to a browser and say “hey the odd way we detect feature is showing a false result, can you fix that?” but there is nothing in a spec to justify my request. Now, gladly, there is.
@supports is the first time there is a spec that holds browser vendors feet to the fire on what their APIs say they implement. it’s fine to carry skepticism, but it would be more productive to file bugs if there are actual problems than expect us all to use javascript for feature detection of css features for the unforeseeable future.
I’ve had engineers from Mozilla, Opera, Google, Apple, and Adobe fix issues when feature detection exposed a false positive (sometimes when CSS parsing succeeded and rendering failed). This is important to them. And now we can share this feature and keep eachother honest. Browser vendors want this feature and want to deliver accurate information; this is why it was introduced by them.
@supports is a big improvement but could be so much better. Yes it’s going to help us but we should still expect more. Why give a half-arsed solution when something more solid could be provided.
@[Keri Henare](http://kerihenare.com/), there is nothing half-assed about @supports. To a browser, parsing the CSS and rendering its results should be directly associated. It’s just how the browsers are written and features are introduced. If there is any inconsistency, then it’s a bug, you will report it, and the browser will fix it.
@supports just seems redundant. I can already do this by simply overriding fallbacks:
body {
background-color: blue;
background-color: cornflowerblue;
}
If my browser doesn’t understand cornflowerblue as a color name, it will just skip that line. Along with precedence rules, the function of @supports is already accomplished implicitly. I don’t see any advantage to specifying it explicitly like this.
@nullability. Yeah for a lot easy usecases, true. But you can’t can’t do
@supports not ( display: flexbox ) {
body { width: 100%; height: 100%; background: white; color: black; }
#navigation { width: 25%; }
#article { width: 75%; }
}
with that style.
@nullability yes, @support isn’t needed in that example. However, you may wish to alter widths and margins if flexbox is supported, as well as using flexbox.
@paulirish hah, snap on flexbox
Paul, if I’m antagonistic, it’s towards the implied promise of the name
@supports
—as I said, if it were called (say)@parses
, that would be much less bothersome, because it would tell authors what feature detection actually means. It would set an appropriate level of expectation. By implicitly promising more, it sets itself up for failure. Or so my experience and instinct tell me. If I’m wrong, so much the better for us all.Reposted from here: http://branch.com/b/unsupportable-promises
Browsers are already delivering unsupportable promises today. @supports is the first effort—a combined effort of web developers and browser vendors, at that—that aims to introduce any sort of accountability for these promises. Some accountability is significantly better than no accountability, even if it’s no guarantee for perfection (which nothing ever is).
One thing I’d love to hear from the people expressing skepticism over @supports is an alternative browser-native implementation. Just renaming it to “@feature” is weird to me; if anything, I’d want it to read @supports-feature. @parses / @recognizes basically adds nothing to the development table that Modernizr’s tests don’t already cover, other than working with JS disabled. They still add no more accountability, which is the big difference with @supports.
Eric, I strongly disagree on the matter of implicit promises here. @supports is significantly more explicit, whereas right now browsers are delivering nothing but implicit promises about their stuff working. And you and I have been around long enough to know how reliable browsers’ implicit promises on these issues has been. @supports is, again, way more explicit than anything else. That introduces a huge new element for them to consider when exposing new APIs.
Not throwing a fit about encountering
background-size: cover;
is a very implicit promise that it works. But it doesn’t necessarily.@supports (background-size: cover) {…}
is a much more explicit promise.@Ben
There are two problems with this definition.
1) There aren’t enough people writing test suites. There are rarely cross-vendor test suites for new features and so we can’t evaluate well the conformance of an implementation
2) Nobody would pass. For more mature test suites, 100% conformance rarely happens. See http://samples.msdn.microsoft.com/ietestcenter/ as evidence that “stable” css3 features rarely pass these suites.
Faruk, I guess I don’t believe the accountability will be any greater with
@supports
than it would with@parses
—the latter of which, even if it didn’t add anything to what Modernizr does, would most likely be far more performant. (This is already the case with touch events, for example.) Accountability is a fine idea, and we already have it, thanks to open bug reporting systems, Twitter, blogging, and what-have-you. When a browser tries to do something with a property:value pair, we know it’s trying to support it, and if it does badly, it gets called out.@supports
doesn’t add much to that except create the illusion that the testing, calling-out, and fixing has already happened.And I can just imagine what will happen when something that’s already recognized with
@supports
in multiple browsers is found to be flawed and in need of change. Does the WG (and browser vendors) decide to break deployed sites, come up with some other syntax, or just ignore the problem? “We have customers!” will once again ring forth, is my guess.As for what I propose as an alternative, well, you’ve already heard it: call feature detection what it really is. You just happen to dislike the idea, which is fine. I didn’t actually expect this to be a popular point of view.
I’m still not convinced of this. Given today’s implementation, how would either of these hold up?
@supports (position: fixed) { … }
@supports (overflow: hidden) { … }
Given the number of hacks/polyfills we need to implement these two properties effectively, I’m not sure how
@supports
is an improvement on the current landscape. It feels like codified inaccuracy, especially since the “usable level of support” is left to the implementing UAs.I’m not down on the idea of moving CSS feature detection out of JavaScript, mind. Honestly,
@supports
would be a huge, huge improvement over, say, the first half of thisposition: fixed
polyfill. But anything beyond that feels like it should be backed by conformance testing, or we’ll be back where we started.@supports gives browser & device vendors the ability to say “Hey, we made this cool new thing, but it doesn’t support position:fixed at this time” and accurately expose that sudden lack of support to websites in a proper, clean fashion.
Is it too late for the existing examples of pos:fixed or overflow:hidden? Sure. But better to start late than never. This facilitates better behavior going forward, which is what spec development is all about.
But will @supports mean that we will have to use it before using any css value?
@paulirish gave the following example:
@supports not ( display: flexbox ) {
body { width: 100%; height: 100%; background: white; color: black; }
#navigation { width: 25%; }
#article { width: 75%; }
}
but what if color:black is not supported on some (wierd) browser? [and being “@supports complient” if you test for @supports (color:black) it would actually fail.
So should this be:
@supports ( (not (display: flexbox)) and (width:100%) and (height:100%) and (background:white) and (color:black) .... ) {
body { width: 100%; height: 100%; background: white; color: black; }
#navigation { width: 25%; }
#article { width: 75%; }
}
It feels odd that you have to spell out everything that you want to do…
Maybe any @supports statement should also imply support to any mentioned property:value combination in the applyed rules ?
I understand the reasoning claiming that that this would make browsers accountable for actually supporting what they claim to support,
but at the same time it would mean that it makes it easier for them to relase “half-backed” versions as long as they properly state that they are indeed “half-backed”.
Thanks for the perspective Eric. I didn’t seen any comments that address your biggest concern that this solution – unlike vendor-prefixes – restricts the WG’s ability to make changes, however necessary.
@Eyal – no, not at all.
This is an optional extension to the existing CSS spec, which simply conditionally applies the stuff in it. this would still work as usual.
body {
color: black;
}
CSS has fault-tolerance at its core. If a browser doesn’t support `color:black`, the rule just won’t be applied. It’s just like using Modernizr, but with finer precision and it *will* be more accurate.
@Sean – this doesn’t restrict WG at all… if anything it gives them more power to change things. A change in a spec? Fine – I can just do:
@supports (feature:oldsyntax) {
// Styles for old syntax
}
@supports (feature:newsyntax) {
// Styles for new syntax
}
Paul, Eric is saying that
@supports
is at best a misnomer.I’m saying that those are the conditions under which I’d gladly make regular use of such a construct.
And to clear up an ambiguity — I’m not saying the privilege would go to a vendor that supports the whole MODULE, just to the vendor that implements the property as spec’d and utilized by the stylist.
In other news, writing software is hard. Film at 11.
Pingback ::
Unstoppable Promises | David Tufts
[…] » Unstoppable Promises […]
For a real live example where this could be really useful in a progressive enhancement way, take a look at Off-canvas horizontal lists
@media all and (max-width: 30em) {
@supports (overflow: scroll) {
.items {
width: 100%;
overflow: scroll;
}
.items .item-list {
width: 600%;
}
.items .item {
width: 15%;
}
}
}
This is spot-on. Very big +1 for “@parses” instead of @supports”. That would alleviate a little bit of the pain and confusion here, at least in terms of terminology.
I have to say.. having never looked at this spec before now, when you asked me to guess what @support would mean, I guessed pretty much exactly what it ended up being. But I have a background in testing browser standards compliance, so my guess was based on what’s logical from the point of view of implementations, not the point of view of the average web developer. @support basically hooks into a piece of logic that browsers have already been doing for ages.
I’d be fine with @feature, or maybe @processes (which conveys what the browser is doing better than @parses, IMO).
Unfortunately for web developers, there’s no way for a browser to reliably know how well it supports a given feature, so we’ll never (or should never) have a rule for 100% support. What @support is testing is whether the browser thinks it knows what it’s doing with the CSS rule, and that’s probably the best we can get.
Pingback ::
CSS note | Native CSS Feature Detection With @supports
[…] meyerweb […]
Pingback ::
Responsive Design Weekly #49 | Responsive Design Weekly
[…] Unsupportable Promises […]