I recently tested media queries in both mobile and desktop browsers, and found that the desktop browsers are making several serious mistakes in their implementation that threaten to pull apart the mobile and desktop worlds.
I feel the desktop browsers should defer to their mobile cousins in viewport matters, since mobile is the more complicated use case and a de-facto standard is emerging. Their recent actions threaten this process. The previous article treated rounding errors and problems with the width media query. This article will treat the very complicated DPR problem.
Briefly, desktop browsers recently changed the meaning of device-pixel-ratio to zoom level. I feel that this information should be moved to a new JavaScript property, media query, and JavaScript event, because squeezing it in with the existing mobile-based device-pixel-ratio properties and media queries has all kinds of odd consequences.
Fair warning: this is complicated, both because of the inherent complexity of the subject and because desktop browser implementations differ. So bear with me and re-read parts of this article if necessary.
We start with a brief summary of the aspects of the mobile viewport that the desktop browsers are trying to port.
width: 20%
are calculated. Equal to the CSS initial containing block.width=device-width
the layout viewport acquires the dimensions of the ideal viewport.window.devicePixelRatio
in JavaScript. This is a constant, since neither the physical dimensions nor the ideal viewport dimensions ever change.window.devicePixelRatio
. Works in all WebKit- and Blink-based browsers.window.devicePixelRatio
.
Works in the non-WebKit-based browsers.Test device-pixel-ratio and resolution. Safari and Firefox form the extreme flanks of this set of incompatibilities. IE and Chrome/Opera are in between them.
All desktop browsers except for Safari now use window.
and the device-pixel-ratio and resolution media queries to denote the current zoom level of the page. That is, if the current page zoom is 150%, window.
becomes 1.5, and the device-pixel-ratio and resolution media queries reflect that.
I have several problems with this approach:
screen.width
must follow that definition (and become a variable instead of a constant) for total consistency.I read through this thread on www-style about the DPR changes. See also this Chromium bug report and this discussion.
So far, I am insufficiently swayed by the Google arguments in favour of the change. There’s a fairly high amount of “surely in situation X this would be useful,” and not so many practical examples.
The argument mostly uses canvas as an example, and states that with the new DPR definition canvas elements (as well as regular images) become crisper, since the web developer can calculate how many physical device pixels are available to show the canvas or image in. I suppose there’s something to this.
Nonetheless I would like to offer a counter-argument in the same spirit. If a user zooms in on a desktop website, this usually has to do with eyesight. The font is too small, or other details of the page may not stand out sufficiently.
I doubt whether such a user would be able to appreciate the crispness of images and canvas elements. He doesn’t see these details very well anyway, witness his decision to zoom in. So we can as well forget about the extra crispness that the new DPR settings make possible.
Besides, changing DPR has unexpected consequences. Currently, web developers assume that DPR is a constant, and they base their code on that assumption. More importantly, the responsive image discussion assumes the same.
What’s going to happen to current DPR-using sites on desktop? Will all images and canvases change (and be downloaded) as soon as the user zooms in on desktop?
Granted, that won’t happen nearly as often as it does on mobile, but initially a user may go through a few zoom levels before settling on the one she likes best. Will desktop browsers be constantly reloading assets throughout that process? That seems like a waste of bandwidth to me. And it could be terribly confusing. See one example of such confusion as reported to Mozilla.
All in all I feel that the browser vendors haven’t thought through these consequences before making the change.
On mobile, DPR is the ratio between the physical number of pixels on the device screen and the dimensions of the ideal viewport — the one you get when applying width=device-width
.
We have physical pixels on desktop screens, all right, but what is the desktop equivalent of the ideal viewport? It doesn’t really exist, since it’s a concept that only makes sense in the mobile context.
Still, the DPR definition requires a desktop ideal viewport to be invented. As far as I can tell, the desktop ideal viewport is “the dimensions, in CSS pixels, of the viewport the browser would have if it were maximized on the screen, without browser chrome.”
And no, that doesn’t make sense to me, either. We have no need for this information.
By itself the desktop ideal viewport definition is silly but harmless. The problem is that on mobile screen.
is starting to mean “width of the ideal viewport.” Not all browsers have gone over yet, but it’s definitely an emerging standard.
So if we’re consistent in our porting to the desktop of the ideal viewport, screen.
would have to mean “the width, in CSS pixels, of the viewport the browser would have if it were maximized on the screen, without browser chrome.”
That is exactly what it means in IE and Firefox. If you zoom in, screen.
changes. Please take a moment to recover from this serious WTF moment.
I admit it, this is consistent. If you port mobile concepts to the desktop anyway, go all-in and port them all. And ignore the fact that they don’t make sense in an environment they weren’t created for.
Chrome and Opera do not follow IE and Firefox here. screen.
remain constants. From a strictly theoretical perspective they are wrong, but from a practical, sanity-based perspective they did the right thing.
It might be time to add a new property, screen.physicalWidth
. It would always denote the number of physical device pixels on the device’s screen, regardless of zoom level or retina screens or anything else.
This would be a boon on mobile, with its shifting definition of screen.
, but if the desktop browsers are going to change that definition, too, it becomes even more important.
Granted, beyond analytics scripts there is no huge use case for such a property — but there never was much of a use case for old-style screen.
, either.
What about the device-width media query? It is slaved to screen.
, for any definition of screen.
, in ALL mobile browsers.
Firefox, again, is consistent in its implementation. It changes the device-width media query when you zoom. The effect is totally weird, but defensible from a theoretical perspective.
IE does not follow Firefox here. Although it changes screen.
with zooming, the device-width media query retains the original, un-zoomed value.
This breaks the equivalency principle (see part 1) in a big way. Suddenly, the media query and the JavaScript property give different information, which prevents web developers from switching from one to the other if they so desire. (OK, nobody is interested in the screen width anyway. But it’s the principle of the matter that counts here.)
Whatever else happens with DPR on the desktop, the IE team must solve this bug. It must either support the Firefox way, where both media query and JavaScript property change all the time, or the Chrome way, where they don’t. Changing the one but not the other is not allowed, though.
Zooming on desktop is totally different from zooming on mobile. On the desktop, the (layout) viewport becomes smaller when you zoom in, since less CSS pixels now fit in the browser window. On mobile, the layout viewport is unaffected, and only the visual viewport changes size.
On mobile, DPR is a constant. It is unaffected by zooming since a zoom action does not change either of the two dimensions DPR is a ratio of. Existing implementation expect this behaviour.
On desktop, DPR is the current zoom level. It changes with a zoom action (or rather, it’s supposed to; we’ll get back to that later), and is thus not a constant. It also forces browser vendors to change the definition of screen.
and make it a variable.
If the use cases and the contexts are so radically different, and if existing implementations will break, why do desktop browser vendors try to squeeze the zoom level into a property that was meant for another context?
What people (or at least, browser creators) want is the zoom level, and not some hypothetical ratio that requires you to mess with screen.
, too, and that breaks compatibility with mobile browsers for no good reason.
So create a new property.
I strongly feel that a new property such as window.zoomLevel
and a slaved zoom media query would be a much better solution to the problem. Its value would be the new-style desktop DPR value.
Granted, such a new property would not be a constant, either, and content would be constantly reloaded during the zoom process. I don’t see an easy solution — this is a fundamental part of how new-style zoomLevel works.
I do see a complicated solution: a zoom event. If we had it we could only apply responsive images when the zoom event hasn’t fired for, I don’t know, five seconds or so. That won’t work in all circumstances, but it’s better than nothing.
I feel a new property, media query, and event would have serious advantages over the current situation.
Most importantly, existing DPR use, which is wholly dependent on DPR being a constant, would continue to work as expected on desktop. With the current implementation existing content would break in interesting ways.
The new property would also be usable on mobile, where it is currently not possible to read out zoom level. The information is much less important and subject to much more change than on desktop, but it could still be useful, if only for analytics.
(Well, it is possible to read out the mobile zoom level by dividing layout viewport width by visual viewport width, but the result may not match the meta viewport *-scale directives. This is a complicated topic that needs special care and attention.)
Also, it would not require browser vendors to redefine screen.
as a variable. That is just totally weird.
Finally, it gives web developers a choice between old-style DPR detection and new-style zoom level detection, instead of squeezing the two disparate use cases into one property and media query and daring web developers to make sense of the readings. They’ll likely resort to browser detects.
In IE and Firefox the DPR changes whenever the user zooms, and all related media queries change, too, subject to the rounding bugs described in part 1.
If you’re convinced that desktop DPR is a good idea this is exactly how it should work. The width media query also changes when you resize a page, and so does the JavaScript property it’s slaved to. So DPR should work the same.
Blink (which powers Chrome and Opera) does it wrong. It reads out the zoom-based DPR for media queries at the moment the page is loaded, and then keeps to those values even when the user zooms. So if you enter the page at 100% zoom and then go to 80%, the media queries still pretend you’re on 100% zoom, and you have to reload the page to get the right DPR of 0.8. Note that this only goes for the media queries, and not for the JavaScript property.
But that’s not all. It turns out that this static implementation of DPR only works up to a certain “border value.” When this border value is reached the media query and window.
suddenly change without a reload being necessary — once.
Once you’ve passed the border value they become static once more and don’t react to zooming until you reload the page or go below the border value.
Try it here. Make sure you’re at 100% zoom and load the page. Then increase the zoom step by step. At first the media query will report 1dppx regardless of zoom level, but that changes when you hit the border value. Once you do, reload the page and continue, then return to the border value again.
What exactly is this border value? Glad you asked. I tested five Blink-based browsers, and between them they support four border values:
Wow. Just wow. You can’t make this up. Nobody would believe you. The worst part is: it might actually be a feature, and not a bug.
Update: This is a bug, and the Chrome team is working on a solution.
We have seen the following:
screen.width
changes as the user zooms. That’s very odd.screen.width
and the device-width media query. This should be fixed.I would urge browser vendors to implement zoom level detection as totally separate media queries and JavaScript properties that save existing content from breaking and do not require porting the ideal viewport to desktop, where it does not make sense. And they should fix their bugs.
This is the blog of Peter-Paul Koch, web developer, consultant, and trainer.
You can also follow
him on Twitter or Mastodon.
Atom
RSS
If you like this blog, why not donate a little bit of money to help me pay my bills?
Categories: