So you want to create a mobile web app that doesn’t look like it’s from 2007 and loads fast to boot with, ideally looking and working just like a native app.
Now you might heave heard that Facebook famously switched to a native app citing performance concerns. However, you’re probably not Facebook and pushing terabytes of data around—most mobile apps just display some data and provide a way to input data (mostly just text) and that’s about it. Web apps do a great job, and you don’t have to develop a native app for each platform you want to support. Plus, you get to use skills you already have.
There’s three main areas that making a “native-like” mobile web app can be broken down to:
- Styling, specifically typography, hairlines, transparencies and animations
- Behavior, including saving the web app to the home screen, making the status bar “part” of the app and reacting to on/offline events, and fast-loading
- Cross-device compatibility, so the app runs on Android and other devices as well
In this first part of a mini-series I’ll go into some details about styling. I’ll use our new mobile web app for Freckle Time Tracking to demonstrate (if you’re a Freckle user: it’s in closed beta right now, ETA very soon!).
It’s all Retina, baby!
iOS7 no longer supports any (iPhone/iPod-sized) devices without a Retina screen and a lot of the design changes are informed by that. Thinner fonts, hairlines and so on are easy enough to do in native apps, but can be hard to do in web apps.
iOS7 uses several different new fonts, and you can choose to support “Dynamic Type”, the new system-wide text size setting. Please be aware of the difference between the font: shorthand setting and the font-family: CSS properties, you’ll need to follow these rules exactly:
- For text in lists and basically any normal text use “font-family: -apple-system-font;” (internally refers to ‘.Helvetica Neue Interface M3′¹) and, if you don’t want to participate in Dynamic Text (see below) force set size 17px and line-height 21px.
- Headers in navigation bars use “font-family: -apple-system-font” with font-weight: bold (internally ‘.Helvetica Neue Interface Medium P4′) at the same size and line-height.
- Very large text, like the one shown here in Freckle’s Timer panel uses “font-family: -apple-system-font” with font-weight: 100 (internally ‘.Helvetica Neue Interface UltraLight P2′) at 72px size. The round colon characters are actually part of a different new font that comes with iOS7, “AvenirNextCondensed-UltraLight” (size 70px).
You can also tap into the new system-wide font scaling setting (Dynamic Text) for better accessibility, with various other
-apple-system CSS presets.
The most important presets are
-apple-system-body. Different from -apple-system-font, all of the above presets set not only the font family but also other properties like size and line-height; and must be used with the font: shorthand CSS property.
-apple-system-font can yield slightly different results (and not the exact same font) as compared to the
-apple-system-* presets.² In most cases, it’s probably better to use the presets (and keep usage of -apple-system-font to a minimum). Don’t forget to set font-size and line-height if you can’t or don’t want to support Dynamic Text.
You can see all presets by attaching the to the iOS Simulator or a test device with Safari’s Web Inspector (you need Safari 6.1 beta, OS X Mavericks or a WebKit nightly!) and use the autocompleter to see all the options you have:
(The system wide text size setting doesn’t affect navigation bars or the really large text that’s shown in the clock application. If you’re looking to emulate these UI elements, use font-family: -apple-system-font and set the font size manually.)
CSS just pixel-doubles border lines on Retina screens, and doesn’t provide an easy way to do a 1 physical pixel wide hairline.
CSS borders can’t just be “0.5px” high (the CSS px unit requires integers, and DOM elements always are positioned on integer-based coordinates as well). One workaround are scaled CSS background images.
In the following example the data URL contains a 2px high PNG image that has a transparent upper pixel, and the pixel in the color I want the hairline to be as the lower pixel. An other way, which has the advantage of allowing you to easily customize the color but is slightly more verbose, is using SVG:
Transparencies and blurs
Using blurs should be limited to very few occasions since it's a major performance hog—you can't have any surfaces that blur out the background layers and still expect scrolling to be smooth for example. But it's useful for example when you want to show an important message in a modal dialog but want to be able to hide it again when an event occurs (which normal alert dialogs can't do).
Blurs can done by setting a
-webkit-filter: blur(8px) on all content and navigation elements, and overlaying an element with a transparent background and no blur.
For partial transparencies in navigation bars, just do gradients that have some transparency on one end and are full opaque on the other. When you know the height of an element, as is the case with iOS-style navigation bars, you can also include the hairline in the gradient, saving you from overlaying multiple backgrounds. Here's the CSS gradient used for the pink gradient in the navigation bar:
In the next installment of this series, I'll take a look into animations.
¹Apple recommends against using the internal names, which while they do work in iOS 7.0, are subject to change in future iOS versions.
²I don't have specific examples but this is information from a reliable source, which I can't name.