September 23, 2013

Not That Close! Avoiding the Landscape Zoom-In Effect on Mobile

When starting a new responsive site at Fuse, our starter theme includes a <meta name="viewport"> tag that tells mobile devices to render the content within a viewport the same width as the physical screen – standard practice these days. But there's a side-effect: a "zoom-in" effect that happens when switching to landscape mode.

Here'€™s the code:


 
<meta name="viewport" content="width=device-width">

If you haven'€™t noticed the zoom-in effect before, give it a try: open a site with the <meta name="viewport"> tag in place (like html5boilerplate.com) on your phone or tablet and switch to landscape. Instead of maintaining the viewport scale and re-flowing the content to fill the additional horizontal pixels, the viewport is simply zoomed to fit and the content is "œblown up"€. This is sometimes not what you want: inflating content on a small screen really makes things feel cramped, and often makes well-typeset text larger than it needs to be.

The reason this happens is that the device-width value represents the device's physical screen width, which obviously does not change when rotating the device. When switching a phone with a physical screen width of 480px to landscape, a viewport set to width=device-width remains at 480px. The browser has more pixels to fill though, so it scales the 480px viewport to fill the screen size, producing the "zoom"€ effect.

The most common solution you'€™ll find when Googling this is to prevent content zooming wholesale by locking the viewport's scale:


 
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

This solves the zoom-in-on-orientation-change problem. However, this approach also prevents the user from pinching to zoom, which to me (and most people concerned about accessibility) is a "€œfix"€ that does more harm than good. We need a solution that doesn'€™t take away basic mobile browser functionality.

There's a simpler solution: just set initial-scale=1. There'€™s no need for anything other than this:


 
<meta name="viewport" content="width=device-width, initial-scale=1">

HTML5 Boilerplate recently updated their HTML template to add the initial-scale property; it just hasn'€™t been implemented (at the time of writing) on their public website. If you're using an older version of HTML5 Boilerplate, consider updating your meta viewport tag.

Interestingly, setting a fixed value for the width property achieves the same effect in almost all mobile browsers:


 
<meta name="viewport" content="width=320, initial-scale=1">

The Mozilla Developer Network article on the <meta name="viewport"> tag explains that for "€œpages that set an initial or maximum scale, the width property actually translates into a minimum viewport width."€ (emphasis mine).

This allows the viewport to "€œstretch"€ to fill larger screens while preventing the zoom-in effect. Why this happens with a fixed value for the viewport width but not the device-width value is a little unclear. The behaviour appears to come from browsers dealing with early (ab)uses of the meta tag in which developers specifically targeted the iPhone's portrait width. In order to render sites on other screens sizes, browsers maintain the 320px scale but force the viewport wider than the author-specified width. This appears to be consistent across mobile browsers. Again quoting the MDN article:

Many sites set their viewport to "width=320, initial-scale=1" to fit precisely onto the iPhone display in portrait mode] This caused problems when Fennec [mobile Firefox] 1.0 rendered these sites, especially in landscape mode. To fix this, Fennec 1.1 will expand the viewport width if necessary to fill the screen at the requested scale. This matches the behaviour of Android and Mobile Safari, and is especially useful on large-screen devices like the iPad.

In summary, just put this in your <head>:


 
<meta name="viewport" content="width=device-width, initial-scale=1">