55 Minutes

Welcome to the 55 Minutes blog.
55 Minutes is a web development consultancy in San Francisco building apps for design-led businesses and startups. On this blog we discuss the technology tools of our trade: Ruby on Rails, Django, Sass, OS X, and more.

Media Queries and Browser Zoom Working around a WebKit bug

Just when we thought we understood media queries and the relative nature of CSS pixel measurements, along comes a subtle bug in WebKit that throws everything off. In this post we look at the nature of the WebKit desktop zoom problem and find an elegant solution using the latest version of Sass.

Designing with pixel breakpoints

Designing for the web using pixels makes a whole lot of sense: after all, the web is viewed on screens made up of pixels. With the advent of responsive web design and the resurgence of fluid layouts, we’ve had to reconsider: perhaps we also need a healthy dose of percentage measurements for layout, and ems for typography.

Nevertheless, it has been widely accepted that pixels are the easiest and best way to specify media queries. Ethan Marcotte popularized the notion of breakpoints in his book, Responsive Web Design, and offered some examples: 320px, 480px, 600px, 768px, 1024px, 1200px, for various devices and orientations. Each project of course is different, with the breakpoints depending on the nature of the design and the intended audience.

The point is that devices are measured in pixels, our tools produce pixels, and as designers we usually think in pixels. Defining media queries like this comes naturally:

/* For 10-inch tablets like iPad, held in portrait mode */
@media (min-width: 768px) {
  ...
}

What about retina displays and zooming?

But if we define a 768px breakpoint, what does that really mean? The first two generations of iPad, for example, were exactly 768 pixels wide in portrait orientation. But the new iPad is now double that: 1,536 pixels. And anyway, if the user gestures to zoom into a web page, a page designed for 768px width suddenly becomes physically much bigger. What do these new screens and zoom features mean for media queries that are defined in pixels?

Our experience using these devices tells us that pixel-based media queries just work. Retina iPads render the web just like older ones did, only sharper. Zooming with your fingers doesn’t cause media queries to suddenly change.

Why is that? It’s because the px unit of measurement in CSS represents logical pixels.1 CSS pixels do not match 1:1 with physical pixels on our screens. On an iPad 2, 1px in CSS is 1 pixel on the screen. On the retina iPad, that same 1px is actually 2 pixels. And when you pinch to zoom in, it becomes even more.

Media queries use the same px system as the rest of CSS, so zooming and retina pixel-doubling effectively zoom and pixel-double the media queries as well. They just work.

Or at least, that’s how pixel-based media queries should work…

Media queries and browser zoom

Pixel-based media queries work great on mobile devices, but Lyza Gardner at Cloud Four recently noticed that these media queries don’t work as expected in desktop browsers when the browser zoom feature2 is used:

I’ll show you a little experiment. I’m using the Chrome browser, and I’m viewing our site with a window about 670 pixels wide… [N]ow I’m going to use the Zoom In command twice to make my text larger.

The layout breaks in a spectacular fashion.

What’s going on here? Is our understanding of CSS pixels incorrect? Lyza explains that using pixels for web design is a flawed practice, and we should throw pixels away in favor of ems. Indeed, changing the media query to ems fixes this particular problem. Why?

/* This works (25em * 16 = 400px) */
@media all and (min-width: 25em)
    
/* But this doesn't */
@media all and (min-width: 400px)

It’s actually a flaw in Safari and Chrome

In fact, as Alastair Campbell points out in his own post on the topic, this behavior is entirely due to a bug in the desktop versions of WebKit (in other words, Safari and Chrome):

…that’s a bug in webkit. If you try the same in Firefox/IE/Opera, pixel based media queries work great with zoom. It just happens that a webkit bug for zooming EM based media queries has had some work, but not for pixels.

WebKit, the browser beloved by web designers everywhere for its support of web standards, has been out-smarted by Firefox, Opera, and Internet Explorer? Now that’s interesting.

Theory vs practice

In theory, pixel-based media queries should work across all devices. In fact, on mobile browsers and in Internet Explorer, Firefox and Opera on the desktop, they work exactly as advertised. But WebKit on the desktop does the wrong thing.

Em-based media queries, however, work everywhere. So do we abandon pixels? We’ll have to keep a calculator handy and start writing all our media queries like this:

@media (min-width: 48em) /* 768px ÷ 16 */ {
  ...
}

@media (min-width: 75em) /* 1200px ÷ 16 */ {
  ...
}

Not particularly elegant, is it?

Sass to the rescue

At 55 Minutes, we are big fans of Sass, an extension of the CSS language that adds time-saving features. Other designers love it too. As luck would have it Viljami Salminen recently shared this his enthusiasm for Sass with this tip:

One of the most useful features for me has been the ability to convert pixels to EMs without the need to use calculator.

Useful indeed! His post offers a simple Sass function that looks something like this:

@function pem($pixels) {
  @return #{$pixels/16.0}em
}

We can use this function to make em-based media queries a lot easier to write.

A quick note on Sass 3.2

The Sass-based solution we’re about to propose actually only works with Sass versions 3.2.0 and higher. Mason Wendell explains the situation in a great article on the The Sass Way blog about improved media query support in Sass 3.2.

As of this writing, Sass 3.2 has not yet been released. However you can get the 3.2.0 alpha, which has worked great for us.

gem install sass --pre

Or with a Gemfile:

gem 'sass', '~> 3.2.0.alpha'

Our Sassy solution

With the previously mentioned pem helper function, we can keep designing, thinking, and coding in pixels, but write our stylesheets in a way that automatically generates the appropriate em measurements for media queries. Using scss syntax:

@media (min-width: pem(768)) {
  ...
}

When compiled to plain CSS and sent to the browser, we get:

@media (min-width: 48em) {
  ...
}

Perfect! Now our stylesheets have em media queries, which will work great even in Safari and Chrome. And we didn’t even need a calculator.

Notes

  1. @ppk recently gave an excellent presentation here in San Francisco called A pixel is not a pixel (5.8 MB PDF) that fully explains pixels and viewports in mobile and desktop browsers. This is a worthwhile read for everyone designing for the web. A video recording of his presentation is also available.

  2. The current browser zoom behavior is a relatively recent phenomenon. Earlier browsers would zoom the text only, leaving images and CSS pixel dimensions unchanged. You can still access this old functionality: for example, in Safari enable the Zoom Text Only option from the View menu. In this blog post, we’re concerned only with the modern zoom behavior, which scales all elements of the page.

comments powered by Disqus