Sencha Inc. | HTML5 Apps

Blog

Resolution Independent Mobile UI

August 23, 2010 | David Kaneda

…or… A pixel is not a pixel. In my last article about the benefits of SASS and CSS3, I briefly touched on a technique we use in Sencha Touch to make our interfaces resolution independent. Today I will be expanding on that technique, so you can start to make your own scalable UI elements for WebKit. Photo comparison of a high pixel density device, an iPhone 4, and a less dense device, the iPad.

A Brief Introduction

This is not an article on adaptive layouts or responsive web design. This article is specifically about pixel “density”, a relatively new concern to digital designers. In the mobile environment, screen resolution has been quickly advancing, resulting in larger and larger “pixels per inch” (ppi). Take, for example, the image above. This is the same page, rendered by the same browser, on the iPad and iPhone 4. Note how the button looks smaller on the iPhone. This is because the iPad has a pixel density of 132ppi, while the iPhone 4, with its Retina Display, has a ppi of 326. The iPhone 4, however, automatically scales pages 2×, so we are effectively working with a ppi of 163. This is still about 15% denser than the iPad display, which means the physical display of the button button is about 15% smaller. This pixel density must be taken into account when designing cross-platform mobile applications, for two primary reasons:
  • If you design a button for a 132ppi (like the iPad), it will come out much smaller for higher ppis, which could result in it becoming harder to tap and possibly render it unusable.
  • Images and UI elements must be scaled intelligently, to avoid pixellation and blurriness.

The General Concept

The overriding concept behind this technique is to use ems — a typographically relative measurement unit — to size your elements. Wikipedia describes an Em as such:
This unit defines the proportion of the letter width and height with respect to the point size of the current font… This unit is not defined in terms of any specific typeface, and thus is the same for all fonts at a given point size. So, 1 em in a 16 point typeface is 16 points.
Because font-size is an inherited property in CSS, we are able to change the scale of any em-based elements by simply changing the parent’s font-size.

Starting Simple: A Button

To put this concept into practice, let’s take a look at a simple button.
.button {
    display: inline-block;
    color: #fff;

    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#C1FA3B), color-stop(0.02, #8FCA05), to(#5A7F03));
    -webkit-border-radius: .2em;
    -webkit-box-shadow: #7EB105 0 0 .03em 0.03em inset, rgba(0, 0, 0, 0.4) 0px 0.1em .2em;
    text-shadow: rgba(0,0,0,.2) 0 .05em .02em;
    border: .08em solid #374D02;
    padding: .2em .6em;
}
As you can see, we’ve added rounded corners, an inner glow (using inset with -webkit-box-shadow), a drop shadow, text shadow, and padding to our element — all using em as our sizing unit. The only sizing which is not accomplished with ems is the background-gradient, which must be defined in percentage-based color-stops. As these color-stops are still relative to the size of the button itself, these values are inherently relative as well. To show off some different sizes for our button, we can easily just add a few classes like:
.size_2x {
    font-size: 200%;
}
.size_8x {
    font-size: 800%;
}
The HTML for our demo then looks like this:
<a href="#" class="button">1×</a>

<div class="size_2x">
    <a href="#" class="button">2×</a>
</div>
<div class="size_8x">
    <a href="#" class="button">8×</a>
</div>
Step 1 shows CSS buttons controlled by font-size. We will continue to use these size_2x and size_8x classes through the rest of the examples. Preview this step in a WebKit-based browser.

Caveat: Changing only the text size

The only troublesome part of this method is that you will occasionally want to change the actual font-size of an element, without affecting its overall scale. For this, we can use a wrapper div/span inside of our element, like so:
<a href="#" class="button"><span class="textsize_2x">2× text</span></a>

Images and Masks

The technique can also be used on images. For traditional images, it’s as easy as setting either the width or the height (the other will be automatically applied to remain proportional) in ems or percentages. We must embed a higher resolution photo, to be scaled down to meet the different sizes (we want to make sure we’re scaling down, as scaling up will result in pixellation).
.imgbase {
    width: 4em;
}
And the HTML:
<img class="imgbase" src="duck.jpg" />

<div class="size_2x">
    <img class="imgbase" src="duck.jpg" />
</div>
<div class="size_8x">
    <img class="imgbase" src="duck.jpg" />
</div>
Similarly, we can be apply sizing to background images and masks:
li {
    background: url(resources/duckbullet.png) 0 .3em no-repeat;
    background-size: 1em auto; // sets width to 1em, then height automatically
}
Step 2 shows images controlled by font-size. The benefit of this technique is that your images will be the same physical size across different devices, but will have more resolution on the devices that support it. It should be noted, though, that these images are not truly “resolution independent.” The very concept of resolution independence is impossible to apply to bitmap-based graphics. As mentioned, we created our image at a large size, anticipating that it would be scaled down. If the image were to be scaled up instead, it would become pixellated. The other unfortunate side-effect is wasted bandwidth — by always including a larger version of your image, you’re ensuring unnecessary kilobytes are being transferred. Preview this step in a WebKit-based browser.

SVG to the Rescue

SVG is a W3C specification for vector images which is already implemented in WebKit (though not all versions*). Because SVG is vector-based, it can be infinitely scaled without detriment to quality. This is the best option available for icons, logos, and non-bitmap UI elements.
.logo {
    width: 2em;
    margin-bottom: 20px;
}
.size_2x {
    font-size: 200%;
}
.size_8x {
    font-size: 800%;
}
And the accompanying HTML:
<img class="logo" src="sencha.svg" />
<div class="size_2x">
    <img class="logo" src="sencha.svg" />
</div>
<div class="size_8x">
    <img class="logo" src="sencha.svg" />
</div>
Step 3 shows SVGs controlled by font-size. * Unfortunately, as SVG is not yet implemented in Android, we are not using this technique in Sencha Touch. Preview this step in a WebKit-based browser.

Community Sample: Loading Indicator

Jordan Dobson has implemented this technique to great effect by creating a CSS3-based loading spinner. Check out his demo page, which has buttons to make the spinner bigger and smaller (again, by changing the parent’s font-size).

Changing Size Per Devices

Once you have your entire UI and layout measured in ems and percentages, you can apply the font-size to the body to adjust the overall scale (previously, we were setting the font-size on a parent element of our target). Currently, in Sencha Touch, we use Javascript user-agent sniffing to detect the device, and then add corresponding classes to the body. Once those are added, we adjust the UI resolution like so:
body {
  font-size: 100%; // This accommodates the lowest density, the iPad, at 132ppi.
}
body.x-iphone-os.x-phone {
  font-size: 114%; // This accommodates the iPhone, at 163ppi
}
body.x-android-os.x-phone {
  font-size: 120%; // This accommodates most Android phones, at 180ppi.
}

The future: @media queries

Ideally, we should be able to detect and change style using CSS3 media queries. There is a specific addition to the CSS3 recommendation that would help here, though it has yet to be implemented on iOS or Android — the resolution attribute:
@media screen and (min-resolution: 132dpi) {
    body {
      font-size: 100%; // This accommodates the lowest density, the iPad, at 132ppi.
    }
}
@media screen and (min-resolution: 160dpi) {
    body {
      font-size: 114%;
    }
}
@media screen and (min-resolution: 180dpi) {
    body {
        font-size: 120%;
    }
}
// Not possible yet :(
There is one media query attribute of note which iOS recently added, called device-pixel-ratio. As mentioned at the start of this article, the iPhone 4 (with Retina Display) automatically scales pixels 2× on web pages. Developers can check for the Retina Display with a query like this:
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    // CSS specific to Retina Displays
}
Aral Balkan has some great thoughts on taking advantage of this rule in his article, How to make your web content look stunning on the iPhone 4’s new Retina display. Step 4 showing all CSS buttons, images and resolution independent scalable vector graphics (SVGs) controlled by font-size. Preview this step in a WebKit-based browser.

Conclusion

As device displays become denser and denser (did someone say Retina Display iPad?), finding the right method for creating resolution independent UI elements will become more and more important. Using ems and percentages is one such method, that we currently think is the strongest, but I’m curious: Do you know of others? Is there a change to the CSS3 spec that you think would improve this process?

There are 36 responses. Add yours.

Jay Garcia

4 years ago

Really awesome to see this.

Joe McCann

4 years ago

Brilliant and thank you.

Damon Oehlman

4 years ago

Thanks for the post David.  Nice to see some practical examples for dealing with varying pixel densities on devices.

Willem de Slimme

4 years ago

Flash has been scalable since 1985, and without the need for a single line of code…

Awesomee Bob

4 years ago

Willem de Slimme, if you haven’t noticed yet, the most popular mobile device on the planet doesn’t support flash.

James Noble

4 years ago

Brilliant. Perfect timing, working on a related project at the moment.

B

4 years ago

Very timely indeed. Thanks David.

Lim Chee Aun

4 years ago

Nice write, David! I’m just thinking if a better solution would be using CSS absolute units like mm or inch instead of percentage and em’s?

Felix

4 years ago

Here’s another resolution independent loading spinner that uses SVG/VML and works in IE6, too: http://neteye.github.com/activity-indicator.html (jQuery plugin)

Josh

4 years ago

Thank you for such a clear and well written post on a confusing subject. This will be on my “book shelf” for reference.

David Kaneda

4 years ago

Thanks for all the great comments guys — let me know if you have any extra questions/thoughts, I’ll be posting a followup to my blog at some point soon.

Ralph Haygood

4 years ago

There are still an awful lot of px’s in the Sencha Touch CSS, though.  Just yesterday, I was working through the .x-tabbar.x-docked-top rules, because I needed smaller than standard tabs for a certain tab panel, and they’re full of px’s.  That’s presumably because they’re a lot about background image management.  Do you really plan to make the Sencha Touch CSS px-free?

Ralph Haygood

4 years ago

P.S.: I realize the lack of support for SVG in the Android browser is a problem.  So I should have asked, do you really plan to make the Sencha Touch CSS px-free, assuming the Android browser eventually supports SVG?

Joe

4 years ago

Like it. We’ve been working on something like this for our apps, but you added a couple pieces to the puzzle we hadn’t got to yet. A big internet-thanks to you sir.

Auré

4 years ago

Waho… a very clear explaination of a great technique !
Thanks wink

Siki?

4 years ago

Really awesome to see this.

David Kaneda

4 years ago

Ralph, two things:

* I do use the occasional px measurement in Sencha Touch, but only for borders/highlights. These could/may become ems, but ultimately, it’s not the worst thing, as they’re all ‘1px’ and just trying to say “the smallest unit possible,” in a sense (ie. A 1px highlight at the top of a tab). Other places where you see px are likely just in the process of being refactored.
* Using ems and SVG are somewhat different, so the fact that Sencha Touch will not _rely_ on SVG in the near future doesn’t affect that we’ll continue using ems for sizing.

Hope that helps answer your questions-

Ralph Haygood

4 years ago

David, thanks.  I agree 1px borders, highlights, and shadows are little or no problem.  As for SVG, I had in mind it can be used to make background images such as Sencha Touch’s tab image resolution-independent, as at

http://www.helephant.com/wp-content/uploads/2009/08/svg1.html

for example.  But if you’re going to deal with background images in some non-SVG way, even better.

Alex Penny

4 years ago

Is Willem really trying to advocate flash? Hah!

Anyway pretty interesting idea.

Rod Homor

4 years ago

Great article. It was so interesting in fact, that I kept reading it whilst I spilled (accidentally) my morning cereal breakfast milk down my shirt…. LOL.

Thanks, and… really, thanks!

=)

Garrett

4 years ago

Your comment is awaiting moderation.
September 11, 2010 at 10:46 pm

That button looks cool.

Though for the technical advice, I can’t completely agree.

Citing Wikipedia for Ems is fine for amateurs but for a technical publication such as this one, any CSS specification should do (CSS 2.1 – lengths).

Using ems is right, but the rest is no good.

“Currently, in Sencha Touch, we use Javascript user-agent sniffing to detect the device”

Such technique is limited to today’s devices. Where it works, it cannot be expected to be forwards-compatible.

I’m not convinced that the iPhone is scaling undesirably. Sure, it’s inconsistent with iPad, but iPhone users are used to it being smaller on the smaller iPhone.

Use class with semantics in mind. textsize_2x is not a semantic class.

David Kaneda

4 years ago

Thanks Garrett — all good feedback. As web buffs (née standards buffs) we could argue all day about semantics, but ultimately, that doesn’t sound like a fun afternoon. The classnames are terrible of course, but were used purely for clarity in the demos. I definitely don’t recommend using names like that in production. As for the UA sniffing, I mentioned it’s not the best method, but until we have support for resolution in @media queries (or the ability to detect ppi via Javascript) I still think it’s the best can do right now. Thanks again-

John "Z-Bo" Zabroski

4 years ago

David,

“The classnames are terrible of course, but were used purely for clarity in the demos. I definitely don’t recommend using names like that in production.”

A few years ago somebody wrote a great research paper, Design Fragments Make Using Frameworks Easier, that studied 36 code samples found on the Internet, used in open source projects, and showed they all were traceable back to code samples on Sun’s developer website, and that some of the demo code contained sub-par coding practices.  The number of users affected by these sub-par coding practices cannot even be counted.

Bottom line: You should always provide code with your name on it that you would drop-in to production.  Hope this comment changes your practices going forward, and that you will always post production-ready samples.

Robert Green

4 years ago

I agree with Garrett, using proportional measures is good advice but messing with the device resolution at the web UI design level is a bad idea. There are already a huge variety of screen resolutions being used just in desktop devices, why the sudden focus on mobile devices just because iPhone changed their dot pitch and provided a method to detect it?

Web developers need to concentrate on clear and effective communication. By all means present attractive pages that are viewable and useable for a wide variety of host environments but let users deal with device related issues such as different sized screens, number of pixels, aspect rations, dot densities and so on themselves.

For example, my eyesight isn’t what it used to be so zooming is very important when using Mobile Safari. Therefore it is extremely annoying when a site is configured to prevent zooming. A designer can’t know every visitor’s circumstance and can’t accommodate them all specifically, so instead they should try to allow as much freedom as possible when the site is viewed. Taking control of resolutions, orientation, font sizes, whatever is obstructive and thoughtless. Just don’t do it.

Jack

4 years ago

Some of you rightly point out the flaws in doing this, but there may be times when someone wants to do this sort of thing.  Very informative AND easy to understand.  Excellent article!

Kaushik

4 years ago

Great smile

Bart

3 years ago

Great article! Some awesome tips in there for the tool kit.

Tameshwar

3 years ago

Thanks smile Nice tutorial,

Trung

3 years ago

Does this work for ie6? Thanks.

Sahadan,Sahada,Spor Haberleri

3 years ago

Really awesome to see this.

siki?

3 years ago

Waho… a very clear explaination of a great technique !
Thanks wink

vertipper

3 years ago

Thanks for this explanation. I think that we use it on our app… grin

Gregory

3 years ago

Links to examples doesn’t work after your site redesign. Can you fix it up? Or probably somebody have those examples?

Matt Fairbrass

3 years ago

Great article. Just the answers I was looking for. If bandwidth is a worry, would you recommend that we base-64 encode our images directly into the stylesheets?

Krisalyn

3 years ago

Knocekd my socks off with knowledge!

easiphone

3 years ago

At easi phone we specialise in big button phones for the elderly, we provide big button mobile phones and big button telephones suitable for senior citizens, partially sighted and disabled.

Comments are Gravatar enabled. Your email address will not be shown.

Commenting is not available in this channel entry.