Desktop media query bugs 2: DPR and zoom level

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.

The mobile situation

We start with a brief summary of the aspects of the mobile viewport that the desktop browsers are trying to port.

Layout viewport
The viewport relative to which CSS declarations such as width: 20% are calculated. Equal to the CSS initial containing block.
On desktop the layout viewport is as wide as the browser window. On mobile it has a default value of between 768 and 1024 pixels, and web developers can set its width by means of the meta viewport.
Visual viewport
The current size of the part of the site that’s shown on the device screen. Can change wildly as the user zooms in and out. Is not very important in the current discussion, but is referred to a few times.
Ideal viewport
The layout viewport that is ideal for viewing web pages on a specific device. Browser vendors determine the dimensions on a case-by-case basis.
When web developers use width=device-width the layout viewport acquires the dimensions of the ideal viewport.
More information.
DPR
Device pixel ratio. The ratio between the physical number of pixels on the screen and the ideal viewport. Exposed as window.devicePixelRatio in JavaScript. This is a constant, since neither the physical dimensions nor the ideal viewport dimensions ever change.
On old iPhones the screen was 320x480 and the ideal viewport was the same, so DPR is 1. On the 4G the screen was 640x960, but the ideal viewport remained at 320x480. Thus the DPR is 2.
-webkit-device-pixel-ratio media query
Slaved to window.devicePixelRatio. Works in all WebKit- and Blink-based browsers.
resolution media query
Its dppx unit is slaved to window.devicePixelRatio. Works in the non-WebKit-based browsers.
screen.width and screen.height
Used to mean the screen dimensions in physical pixels, but increasingly starts to mean the dimensions of the ideal viewport. The second definition is likely to win out.
More information.
device-width and device-height media queries
Are slaved to screen.width and screen.height in ALL mobile browsers, whichever definition they use for these properties.

Tests

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.

DPR in desktop browsers

All desktop browsers except for Safari now use window.devicePixelRatio 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.devicePixelRatio becomes 1.5, and the device-pixel-ratio and resolution media queries reflect that.

I have several problems with this approach:

  1. I do not see the need for a zoom-based DPR.
  2. Desktop DPR is different from mobile DPR, breaking existing implementations.
  3. If you’re serious about implementing DPR and retaining compatibility with mobile browsers you must also port the concept of the ideal viewport, which has no meaning on desktop. The resulting definition is very strange, and screen.width must follow that definition (and become a variable instead of a constant) for total consistency.
  4. Blink has serious bugs in its implementation. IE has a bug in the device-width media query.

Why?

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.

Unexpected consequences

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.

Porting the ideal viewport

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.

screen.width

By itself the desktop ideal viewport definition is silly but harmless. The problem is that on mobile screen.width 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.width 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.width 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.width/height remain constants. From a strictly theoretical perspective they are wrong, but from a practical, sanity-based perspective they did the right thing.

screen.physicalWidth?

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.width, 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.width, either.

device-width

What about the device-width media query? It is slaved to screen.width, for any definition of screen.width, 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.width 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.

A new property

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.width and make it a variable.

zoomLevel

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.width, 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.

Advantages

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.width 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.

The Blink implementation

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.devicePixelRatio 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.

Conclusion

We have seen the following:

  1. The use case for exposing the desktop zoom level in media queries and JavaScript properties is insufficiently substantiated. (There might be a use case. But right now I don’t see it.)
  2. Existing implementations of old-style DPR may break when confronted with the new definition.
  3. Squeezing zoom level into device-pixel-ratio, which has a different meaning and context, is not a good idea. Instead, we should create a separate zoom media query, JavaScript property, and event.
  4. Desktop DPR will change while the user zooms, and that might mean reloading responsive images all the time. There is no good solution to this problem — it’s fundamental to the new approach.
  5. If we insist on squeezing zoom level into device-pixel-ratio we must also port mobile’s ideal viewport to the desktop, where it gets the rather idiotic meaning of “the dimensions, in CSS pixels, of the viewport the browser would have if it were maximized on the screen, without browser chrome.” Whatever.
  6. Worse, porting the ideal viewport to desktop means that screen.width changes as the user zooms. That’s very odd.
  7. IE breaks the equivalency of screen.width and the device-width media query. This should be fixed.
  8. Blink has serious bugs in its desktop DPR implementation. They 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: