In my experimentation with HTML5, I ended up making Trebek, a web app that allows you to play along at home with Jeopardy! The idea is that by trying to answer questions before the contestants do and keeping score, you can gauge your chances of making it onto the show. According to two-time winner Karl Coryat, you have an excellent shot if you can score $28,000 or higher on a consistent basis, and Trebek helps keep track of your performance.
You can try Trebek out below. For best results, load it in Safari on your iPhone or iPad, then add it to your home screen.
Tailoring the interface by scaling elements
Trebek’s primary use case is leaning back on a sofa and pulling out your smartphone just as the show’s about to start. But it’s meant to be usable for – and therefore scales to – a full range of devices. You can test this out by loading it in a browser window and resizing to various shapes and sizes.
There are several ways to achieve this, but what I recommend is setting the font-size of the body to 100% and then using em as a unit of measure throughout the rest of the code.
padding: 0.2em 0 0.2em 0;
text-shadow: #000000 0.05em 0.05em 0.02em;
The size of em depends on the font-size of the element. By default, 1 em equals 16 pixels. But by changing the base font-size, you can scale all properties that are using em simultaneously. With the following jQuery, you can update body’s font-size, and therefore all of these properties, whenever the window is resized.
$('body').css('font-size', Math.floor($(window).height() * 100 / 768) + '%');
You can see why using pixels instead might be a bad idea. As an absolute unit, you have to recalculate and update the pixel size of each property individually, each time the window is resized.
On the other hand, using percent to size things is appealing since it’s relative by its very nature. Using percent also means you no longer require an update function for when the window is resized. If you set an image’s width to be 25%, it’s always going to be 25% of whatever, you might assume. However, keep in mind that percentages are local, meaning they’re based on the element’s parent and not a global value. While they’re useful for site layout, they’re less effective for fine-tuned properties like padding and text-shadow. Just try using percents to set the same padding for multiple elements, when their parent elements are different sizes.
While I simply scaled the interface, you might consider media queries for more custom interfaces conditional on resolution, aspect ratio, and orientation. For example, you could provide a one-column interface for smartphones versus a two-column interface for tablets.
Cutting the cord with local storage & application cache
Local storage allows you to store data on the client in much the same way as a browser cookie. It allows you to avoid databases — nice for a lightweight app, particularly one you intend to work offline. One word of warning though: clearing the browser’s cache results in the data being lost. Trebek uses local storage to save scores and keep a history of a user’s performance.
stats = JSON.parse(localStorage.getItem('trebek'));
var now = new Date();
stats[now.toString()] = parseInt($('#score').text());
Application cache stores your essential files so your web app can work offline, useful when your smartphone is in airplane mode or you have an iPod touch sans wifi. For Trebek, the greatest benefit regardless of whether the device is offline or not is that application cache improves startup speed.
The main thing you need is a cache manifest, which is a text file that specifies the resources to be cached for future use. Name this file “cache.manifest”, with the following content, and place it in the root of your web app. Under the CACHE section, you want to list all of the assets you wish to be cached except for the manifest file itself.
# Trebek version 1.0
Each time the web app is opened while online, the cache manifest is checked to see if it’s been changed (such as by updating the version number comment). If a change has been made, this triggers the browser to re-cache the rest of the files. If not, the cache continues to be used.
With the cache manifest in place, you want to declare it in one of your pages by adding the following attribute to the HTML tag.
Finally, make sure that your web server is serving files with the manifest extension as type “text/cache-manifest”. It won’t work otherwise. You can check this using your favorite browser’s developer tool. If the file type is not correct, add the following line to the .htaccess file in the root of your web server.
AddType text/cache-manifest manifest
That covers the very basics of using local storage and application cache to enable offline functionality in your web app. Obviously much has been glossed over, so if you want to learn more, check out Mark Pilgrim’s chapters on local storage and application cache.
Putting on the finishing touches for iOS
Here are a few more steps you can take to put some polish on your newly offline-capable web app. First, save your favicon for multiple resolutions: 48, 57, 72, and 114 pixels. Not only are browsers making greater use of large favicons, but they can be used as icons when your web app is saved to your home screen, giving it a bit more of that native feel.
In the header of your page, add the following elements. The first specifices a favicon for web browsers, and the next three are for the iPhone, iPad, and iPhone retina display respectively. You can skip the
tag to have iOS automatically add its customary gloss to your icon.
<link rel="icon" type="image/png" href="img/icon-web.png"/>
<link rel="apple-touch-icon-precomposed" href="img/icon-iphone.png" sizes="57x57"/>
<link rel="apple-touch-icon-precomposed" href="img/icon-ipad.png" sizes="72x72"/>
<link rel="apple-touch-icon-precomposed" href="img/icon-retina.png" sizes="114x114"/>
If you want to give your web app a custom splash screen, add the following tags as well. The images you reference must be exactly 1004×768, 768×1004, and 320×460 pixels respectively. As far as I can tell, iPhone 4’s retina resolution is not supported yet.
<link rel="apple-touch-startup-image" href="img/startup-ipad-landscape.png" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)"/>
<link rel="apple-touch-startup-image" href="img/startup-ipad-portrait.png" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)"/>
<link rel="apple-touch-startup-image" href="img/startup-iphone-portrait.png" media="screen and (min-device-width: 200px) and (max-device-width: 320) and (orientation:portrait)"/>
Add the following code to tell Safari that your site is intended as a web app, change the menu bar to black, hide the chrome to display the app full-screen, and prevent pinch-to-zoom.
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="apple-touch-fullscreen" content="yes"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
So there you have it. It’s great to see the progress that’s being made with the web in recent years. Although it hasn’t quite reached parity with native apps, for many of the simpler apps you find in the App Store, it’s now possible to create an equivalent experience using HTML5.