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 that desktop browser makers have focused on desktop use cases too narrowly and failed to see that these changes introduce deliberate incompatibilities between desktop browsers and mobile browsers. It’s web developers who have to solve this mess, and it’s likely they can only do so by using browser detects.
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. This blog post and the next ask them to reconsider several ideas.
I identified three distinct problems in the desktop browsers:
document.documentElement.clientWidth/Height
. Instead, they take their cues from window.innerWidth/Height
. This means desktop browsers now treat these media queries differently than mobile browsers.This article treats the first two problems, which are relatively simple. A separate article will treat the very complicated third problem.
Before continuing to the width and height problems, a brief summary of some aspects of the mobile viewport. Note that the definitions below hold in nearly all mobile browsers, and form a de-facto standard.
width: 20%
are calculated. Equal to the CSS initial containing block.Many media queries have their JavaScript equivalents. That is, they are slaved to the values of certain JavaScript properties, allowing web developers to switch from CSS to JavaScript if their sites need it.
JavaScript is sometimes a better tool than media queries; for instance when you have to decide whether to include those Twitter or Facebook widgets or not. Media queries don’t help here, since they can at most hide the widgets. JavaScript is necessary for not loading them in the first place. Read out the layout viewport width, and load the widgets only if that width is above a certain value.
Therefore, the two following expressions MUST be exactly equivalent, and in fact are in ALL mobile browsers:
@media all and (max-width: 533px) { // styles } if (document.documentElement.clientWidth <= 533) { // styles }
It turns out desktop browsers destroy this equivalency by using floating points for media queries, and by redefining the JavaScript equivalent of the width media query.
The rounding bug is simple. It occurs only when you zoom desktop browsers in or out, but in the next article we’ll see that this is becoming an important use case.
Take a (layout) viewport with a width of 533.333333, or a similar non-integer number. Zooming in can give this kind of values quite easily, and desktop browsers handle them badly.
document.
, and, in fact, all JavaScript properties that expose pixel widths of elements or viewports, are integers. The 533.333333 is rounded down to 533. Everybody knows that.
However, media queries use floating points. The 533.333333 is not rounded down, and the value the media query uses is not equal to the value of the JavaScript property. This violates the equivalency principle.
The rounding bug affects the following media queries; in all cases when the user zooms:
The solution is simple: just use integers.
The width media query is one of the two cornerstones of responsive design. (The other one is the meta viewport tag.) Which JavaScript property is it equivalent to?
Well, that depends on your device type:
document.documentElement.clientWidth/Height
.window.innerWidth/Height
. Safari sides with the mobile browsers.Test the width media query here. If you’re on Mac, first set the scrollbars to always appear (System Preferences -> General -> Show scroll bars always). Compare Safari with any other browser.
Since width is THE vital media query for responsive design it should not be slaved to one property on mobile, and another one on desktop.
Why do mobile browsers use document.
? Because these properties contain the information they need.
On mobile, document.
give the dimensions of the layout viewport, while window.
give the dimensions of the visual viewport.
The width and height media query give information about the layout viewport, and thus they must be slaved to document.
. Simple.
This behaviour is very old. As far as I know Nokia and BlackBerry had already implemented it when the first iPhone appeared. Apple copied it, and the later mobile browser vendors followed suit (sometimes after a bit of prodding by yours truly). Only Firefox still doesn’t expose the visual viewport dimensions correctly, but that is supposed to change in 27 or 28 (I forget which).
Why do desktop browsers use window.
? Because of the scrollbar. With this definition they exclude the width of the scrollbar from their calculations.
If desktop browsers would use document.
, and a responsive design would hit a breakpoint, part of the page content might show up under the vertical scrollbar, causing a horizontal one to appear. Desktop browsers want to avoid that.
IE and Firefox have been doing this forever. The change in Blink, which affects Chrome and Opera, is recent, though. The Yandex browser, which uses Chromium 28, still uses document.
as equivalent for the width and height media queries.
Safari, too, sticks with the old definition. But Safari has it easy, since it’s Mac-only. On Mac, the default setting hides scrollbars as much as possible and thus no space is left open for them. Therefore document.
and window.
give the same results. (I manually enabled scrollbars for my tests.)
Despite their behaviour being understandable, desktop browsers have broken compatibility with their mobile cousins. How bad is that for web developers who use the JavaScript equivalent to the width media query?
A web developer used to mobile will use document.
on desktop, too, and will thus be about 12-16px off (except on a Mac). That can be annoying sometimes, but it’s hardly earth-shattering. If this were the only scenario I’d be inclined to let it rest.
What I’m afraid of is the opposite scenario. A desktop web developer uses window.
but is quite unaware that that means something totally different on mobile. His script will misfire dramatically on mobile and severe breakage will occur.
How do we solve the issue? I see three options here:
document.documentElement.clientWidth
and window.innerWidth
on desktop.I reject option 1. The desktop browsers are in the wrong here, since they insert a serious incompatibility in the handling of the most important media query there is. Also, this option severely hampers the freedom of web developers to switch to a JavaScript solution if they so desire.
Option 3 sounds great in theory, but changing the definition of long-established JavaScript properties is not something to be done lightly. Older sites that depend on one property or the other will certainly break.
That leaves option 2: accepting the fact that on desktop responsive designs might occasionally stretch underneath the vertical scrollbar. Sure, some sites will suffer, but web developers can easily fix that by changing some breakpoints slightly.
I’m not happy with option 2, but I see it as the least intrusive. Therefore Trident, Gecko, and Blink should be changed so that the width and height media queries are slaved to document.
.
We have seen the following:
document.documentElement.clientWidth/Height
, as their mobile cousins do, and accept the occasional scrollbar problem.That concludes the simple problems. The really tricky one is the porting of the mobile device-pixel-ratio concept to desktop without taking stock of the consequences. We’ll treat that in the next article.
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: