37signals logo

This is Signal vs. Noise, a weblog by 37signals about design, business, experience, simplicity, the web, culture, and more. Established 1999 in Chicago. Follow us on Twitter for more information on our products.

Jobs:

See more on our Job Board.

Device-scale user interface elements in iOS Mobile Safari Sam Jun 15 2010

20 comments Latest by Micah

When we set out to build finger-friendly controls for iOS devices in Basecamp, two major constraints informed our design.

In Basecamp on a PC, when you hover over a to-do, milestone, or file, you’ll see edit and delete controls. But as has been covered here and elsewhere on the web, there’s no way to hover on a touch device. So our solution to this first constraint is to show the controls when you tap instead of when you hover.

The second constraint is that the controls must be finger-friendly. That is, they should be sized such that they’re always big enough to operate with a thumb, but never too big to fit on the screen.

Our first attempt at these controls turned out to be too small when zoomed out…

…and too big when zoomed in.

There’s a sweet spot where the controls are just the right size for a finger.

We could use Mobile Safari’s <meta name=viewport> tag to fix Basecamp’s page width to this sweet spot, so our controls would always be the right size. But then we’d need to redesign everything else in the app around the new width.

The solution

Instead, we developed a JavaScript technique for dynamically scaling elements based on the current zoom factor. The technique provides a magic class name device_scale that you can apply to any element you want to remain the same size regardless of zoom.

Here’s how it works. The script creates a stylesheet and installs event listeners to watch for changes in the page’s dimensions as a result of panning, zooming and rotation. When the dimensions change, the script recalculates the ratio of device width (number of actual pixels on the screen) to page width (number of virtual pixels on the page) and updates the stylesheet accordingly:

.device_scale { -webkit-transform: scale(/* <ratio here> */) }

To use this technique in your own applications, just include the script and sprinkle in class=”device_scale” as appropriate.

Now Basecamp’s controls are sized the same regardless of how far you’ve zoomed in or out, and we didn’t need to redesign the entire application to account for it.

Here’s the script:

(function() {
  var hasTouchSupport = "createTouch" in document;
  if (!hasTouchSupport) return;

  var headElement  = document.getElementsByTagName("head")[0];
  var styleElement = document.createElement("style");

  styleElement.setAttribute("type", "text/css");
  headElement.appendChild(styleElement);

  var stylesheet = styleElement.sheet;

  window.addEventListener("scroll", updateDeviceScaleStyle, false);
  window.addEventListener("resize", updateDeviceScaleStyle, false);
  window.addEventListener("load",   updateDeviceScaleStyle, false);
  updateDeviceScaleStyle();

  function updateDeviceScaleStyle() {
    if (stylesheet.rules.length) {
      stylesheet.deleteRule(0);
    }

    stylesheet.insertRule(
      ".device_scale {-webkit-transform:scale(" + getDeviceScale() + ")}", 0
    );
  }

  // Adapted from code by Mislav Marohnić: http://gist.github.com/355625
  function getDeviceScale() {
    var deviceWidth, landscape = Math.abs(window.orientation) == 90;

    if (landscape) {
      // iPhone OS < 3.2 reports a screen height of 396px
      deviceWidth = Math.max(480, screen.height);
    } else {
      deviceWidth = screen.width;
    }

    return window.innerWidth / deviceWidth;
  }
})();

View/Share Gist

Looking for a job? Got a position to fill? Check out the Job Board.
Got a web design project in mind? Find a web designer on Sortfolio. Browse by visual style, portfolio, budget, and geographic location.
Over 1 million people use 37signals' simple web-based software to collaborate on projects, track contacts, and organize their business with an intranet.

20 comments so far

Justin Michael 15 Jun 10

This is great stuff! Can I assume this is coming to Backpack soon?

Eric Anderson 15 Jun 10

Seems like a kludge to me. I assume this only works on Steve Jobs approved devices.

Siggi Árni 15 Jun 10

@Eric

That’s what the title says :)

Kevin Ansfield 15 Jun 10

@Eric

Android uses the webkit engine as well, so I assume changes won’t be needed or if so they will be minor

Chad 15 Jun 10

As I understand it: ‘var hasTouchSupport = “createTouch” in document;’ Will return false on Android, so it won’t work as is.

Radoslav Stankov 15 Jun 10

Really good stuff. Especially the play with the dynamic stylesheet :)

Marc 15 Jun 10

I assume when you typed: .device_width { -webkit-transform: scale(...) }

...that you meant to type: .device_scale { -webkit-transform: scale(...) }

???

SS 15 Jun 10

Good catch Marc. Fixed.

Mathias 16 Jun 10

Nice script! In case you’re interested, I slightly optimized it and saved the edited version as a fork of your gist.

Jason Lynes 16 Jun 10

Sam,

Rad solution. Absolutely love.

One question: How does the user know those actions are below a tap? Since tap is also a click, which often takes me away from my view, how can you make that new tap functionality more obvious?

Struggling with similar problems, look forward to seeing this in action.

Aaron M 16 Jun 10

Thanks for the post, that should come in handy.

dude 16 Jun 10

Are you also cooking up a solution for drag&drop e.g. rearranging todo items?

Grover Saunders 16 Jun 10

So can I ask why this has been so long in coming? I appreciate that you have plenty of things to do, and none of us want feature bloat, but if I was a paying Basecamp customer and I waited three years for the interface to be usable on an iPhone….well I wouldn’t be a paying customer anymore. And Basecamp is the product you actually care to update most often!

I didn’t come here just to bust your balls, but this is the main reason I’m no longer a paying Backpack customer.

Jonathan Dickinson 16 Jun 10

Any idea if this works on android?

Joshua Clanton 16 Jun 10

@Grover – I assume they are adding this functionality primarily due to the iPad, rather than the iPhone. That makes sense because there are Basecamp iPhone apps, and optimizing the web interface for iPhone would be redundant.

With the iPad the resolution is desktop equivalent, so only relatively minor modifications like this are necessary.

Cesare 16 Jun 10

@jonathan I bet, since HTML rendering engine ismthe same. But I just bet :)

Grover Saunders 17 Jun 10

@Joshua

Though I appreciate the effort, I’m not sure that’s a reasonable explanation. The very premise of this article is that you need to have a solution that’s friendly when zoomed out or in on an iPhone. As to apps, with one notable exception, all the native apps for 37signals products that I’ve tried (and I’ve wasted a lot of money on every one I could find) on iPhone are extraordinarily mediocre at best. And many of them have only come out in the last year or so.

foljs 21 Jun 10

@Eric Anderson Seems like a kludge to me. I assume this only works on Steve Jobs approved devices.

Yeah, only on the mobile devices that matter most to a web business…

But it should also work on webkit-based Android phones too. I hear those things are very popular in rural Nebraska, though I doubt if, say, they have even 1/5th the basecamp users that the “i” devices have…

Raj 21 Jun 10

I have the same question as Jason Lynes. How do you make ‘tap’ as an affordance for the end user? I am just curious to see the solution.

Micah 21 Jun 10

I’m going to 2nd Jason Lynes’s comment and 3rd Raj’s.

“How do you make ‘tap’ as an affordance for the end user? I am just curious to see the solution.”

Comments are closed