Media Queries

One element of CSS that is becoming more and more important to understand is media queries. The idea is that by evaluating certain traits of the visitors device (screen size, orientation, resolution, etc) you can then customize your CSS accordingly. So in theory, you would build a single site, that would respond intelligently (Responsive Web Design) depending on the visitor, rather than having a ‘full’ site, a ‘mobile’ site, and redirects between them.

The general syntax is that within your <style> code, you have:

@media (parameter_to_test:value){ here is the CSS for this particular scenario }

Here is a recent example I made to demonstrate the concept:

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="data:image/png;base64,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"/>
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Media</title>
<style>
html,body{width:100%;height:100%;margin:0;padding:0;background-color:#f00}
@media screen and (max-device-width: 320px) and (orientation:portrait) {
body { background-color:#FFF; }
}
@media screen and (max-device-width: 320px) and (orientation:landscape) {
body { background-color:#000; }
}
</style>
</head>
<body>
<span style="color:#000">Portrait</span><span style="color:#FFF">Landscape</span>
</body>
</html>

Because this was particular inquiry was actual regarding iPhone orientation changes, you’ll see I have some of the apple- header data (I discuss this further in the Web App post). I like to include that in mobile projects because I find most benefit from being installed to the Home Screen.

If you take this code and view it in your browser (or click here), you’ll get a big red screen because the CSS outside of the media queries has background-color:#f00 but if you view in on an iPhone (which has a width of 320px [see that value in the media queries?]) you’ll have a white background in portrait, and a black background in landscape [note those items in the queries as well].

Update: I realize I didn’t fully dive in to the different parameters one can evaluate. Tonight I was asked how to determine if a device was a) typical non-tablet device b) high resolution non-table device c) tablet.

For an ideal solution one would also consider high resolution tablets (iPad 3) but the concept is still sound:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Media2</title>
<style>
body, html {margin:0;padding:0;height:100%;width:100%; background-color:#000000;}
@media (max-width: 767px) { body {background-color:#0000FF;} }
@media (max-device-width: 320px) and (-webkit-min-device-pixel-ratio: 2) { body {background-color:#00FF00;} }
@media (min-width: 768px) { body {background-color:#FF0000;} }
</style>
</head>
<body>
</body>
</html>

So the page is just a 100% wide, 100% high block, with background color black (#000000). Then the media queries are applied:

1) If the device is 767 pixels wide, or smaller, the background is made blue (#0000FF)

2) If the device is 320 pixels wide, or smaller, AND has a pixel ratio of 2 (which the retina devices have), the background is made green (#00FF00)

3) If the device is 768 pixels wide, or larger, the background is made red (#FF0000)

Notice the order as well, if I had test 2 be the first line, it would succeed, making the screen green, if that was followed by test 1, it would also succeed, changing the color again, now to blue.

Obviously these CSS changes are trivial, but it illustrates how a single page can adapt to the device on which it’s being used (click here to try your device):
Media Queries

For the sake of brevity, I try to place all of the style elements that are NOT impacted by media queries (or default values for those that are, such as the red in the first example, or black in the second) outside of the queries. I have seen pages that have nothing outside of the queries, and then just have the full set inside each query, but I try to avoid repetition (you can also load external CSS files using queries, but the syntax is a little different):

<link rel="stylesheet" media="screen and (min-device-width: 800px)" href="example.css" />

You can also replicate some of these behaviors with javascript, for instance, testing window.innerWidth along with window.orientation, then re-writing some CSS values like so:

document.styleSheets[0].cssRules[0].style.backgroundColor='#0000FF";

Here are some examples of responsive web design. According to Google, nearly 80% of it’s top ad buying companies have not designed their site for mobile devices. Is your site mobile optimized? What interesting examples of media queries have you seen?

Web Apps

So web apps are pretty awesome. Of course they have some limitations like access to device hardware (camera), and they can take a little extra time to load depending on how they’re built, but overall, the time it takes to put together a convincing prototype web app is small and the results can be impressive. And then there are tools to wrap these builds in enough native code that it can really become a native app: PhoneGap, Appcelerator, etc

I’ve been in a few prototype coding competitions recently, and have found myself adopting web app solutions more and more. I also receive a lot of questions about this at work, and find myself pointing people to the same resources over and over, so I thought a post here might benefit someone else. Please do find a number of helpful links throughout the post.

The gist of it (and I should preface that I’m biased towards iOS, but many of these elements port to Android just fine) is that you want to take a web page and modify it sufficiently so that it mimics a native application experience. This entails things like: removing the browser elements, adding an icon, adding a launch/splash screen, handling orientation changes, and so on.

Let’s jump into it…here is some of the header from a typical app I build:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="apple-touch-icon" sizes="57x57" href="icon/icon57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="icon/icon72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="icon/icon114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="icon/icon144.png" />
<link rel="apple-touch-startup-image" media="screen and (max-device-width: 320px)" href="images/iphone_splash.png"/>
<link rel="apple-touch-startup-image" media="(max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)" href="images/iphone_splash_hires.png" />
<link rel="apple-touch-startup-image" href="icon/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="icon/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="icon/ipad_landscape_hires.png" media="screen and (min-device-width: 481px) and (max-device-width: 1536px) and (orientation:landscape) and (-webkit-device-pixel-ratio: 2)" />
<link rel="apple-touch-startup-image" href="icon/ipad_portrait_hires.png" media="screen and (min-device-width: 481px) and (max-device-width: 1536px) and (orientation:portrait) and (-webkit-device-pixel-ratio: 2)" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />

Going line by line: “viewport” tells the device how wide the viewable area should be…it’s important because an iPhone is 320 pixels wide (or 640 points in a retina display) but if you don’t explicitly set the viewport, it acts as if it’s 960 pixels wide (so if your content is 320, you’re getting a zoomed out version).

The “apple-touch-icon” links are icons for: iphone, ipad, iphone retina, ipad retina.
icon sizes

In my sample I made unique icons for each just to highlight that they were different, but really, I’d build one at 144×144, then scale it down and save it accordingly…unless I had some graphical element that fell apart in the smaller icons, then I’d tweak those versions accordingly. Here are some metrics around icon images: HIG

The “apple-touch-startup-image” links are for the splash screen. This is the image that pops up while the app loads. There are some intricacies around this, mostly with iPad. iPhone is straightforward: portrait, 320×460 for iPhone, 640×920 (don’t forget the media portion) for iPhone retina…but iPad is a little odd in that they accept a landscape version as well as portrait, but the images it accepts must always be taller than they are wide, so when you’re making the landscape images, you need to rotate the CONTENT, not the image. Not a big deal, but a critical distinction to make.

apple-mobile-web-app-capable” tells the phone that this page can be installed as an application (using the icon and splash screens we’ve defined)

apple-mobile-web-app-status-bar-style” can set the status bar style. There are a few options (default, black, black transparent) but I usually omit it if I want the default, or go with black if the style of the app is dark.

The stage is set, but we have some steps to get through before showing real content, so in order to let the user know we’re working on it, I like to start my apps with a little loader graphic that is then replaced when the real load is complete:

<body>
<img src="loader_graphic.gif"/>
</body>

So now the framework is there, but there are a few more subtle details before you get to the content. First, in order for anyone to see the icon and startup image, they have to install it. A user does this by tapping the arrow at the bottom of safari (on an iPhone, it’s at the top on iPad), and then ‘Add to Home Screen.’ So you’ll want to keep an eye out for users that can do so, and give them some guidance (more or less politely depending on the purpose…I’ve had builds where if it was not installed, you would only see an animation of how to do so):

installation guidance screen

But often you’ll just see a little tooltip box appear near the arrow as a gentle reminder. So how do we determine when do do any of this? We need to know it’s an eligible device, and that it’s not already installed. For the first portion of that I do the following:

var ipad=navigator.userAgent.match('iPad')?true:false;
var iphone=(navigator.userAgent.match('iPhone')||navigator.userAgent.match('iPod'))?true:false;

So now we know if it’s an iPad, or iPhone (I don’t distinguish between iPhone and iPod here). But is it installed? By testing the navigator.standalone property we can find out.

if((iphone||ipad)&&(!navigator.standalone)){
	//here is where you would handle install instructions like 
	//popping up an image about how to "Add to Home"
	str='please install me by pressing the arrow button at the ';
	str+=ipad?'top':'bottom';
	str+=' of the screen, then selecting \'Add to Homescreen\'';
	alert(str);		
}

So, we’re close, but not quite there because unlike a full computer, your device has an orientation. You’ll want to add a listener for orientationchange to the body, which can be as straightforward as:

<body onorientationchange="updateOrientation();">

in conjunction with a function like this:

function updateOrientation(){
	switch (window.orientation) {
	//while some things will be handled by the phone itself,
	//you may need to change some things manually (for instance,
	//you may have two elements stacked on top of each other,
	//and you'd like them to sit side by side in landscape mode
        case 0:
        alert ('Orientation: Portrait');
        break;
        case 90:
	alert ('Orientation: Landscape');
        break;
        case -90:
        alert ('Orientation: Landscape');
        break;
	case 180:
        alert ('Orientation: Portait');
        break;
    }
}

Obviously javascript is one approach, but some things can be handled with CSS Media Queries, so that’s a powerful tool to understand as well.

One detail about the various versions of iOS, is that CSS’ position:fixed isn’t supported before 5.0, so if I used that (and I do), I want to test for that as well:

if(iphone||ipad) { 
	if(/OS [2-4]_\d(_\d)? like Mac OS X/i.test(navigator.userAgent)) {  
        	alert('This is designed for iOS 5 and above, please update your device for proper functionality'); 
   	} else if(/CPU like Mac OS X/i.test(navigator.userAgent)) {
        	alert('This is designed for iOS 5 and above, please update your device for proper functionality');
    	} else {
		//iOS5+
	}
}

Okay. So the last piece is user interface. If you’re trying to mimic a native app, you’ll probably use elements like a navigation bar at the top, or tabs at the bottom, etc. Here is how I set up a starting point:

#nav {
width:100%;
height:40px;
position:fixed;
top:0;
text-align:center;
background-color:#29abe2;
color:#FFF;
font-size:150%;
padding-top:5px;
border-bottom:thin solid #444444; display:block;
font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
}
#main{ width:100%; margin-top:45px; }
#footer {
width:100%;
height:44px;
position:fixed;
bottom:0;
left:0;
text-align:center;
color:#FFF;
font-size:150%;
margin-top:5px;
border-top:thin solid #444444; 
background:#000000;
}

And when I’m ready to show the app (it’s installed, etc), I just:

document.body.innerHTML='<div id="nav"></div><div id="main"></div><div id="footer"></div>';

And then fill in my content to the main div (I have a function for each view that changes the .innerHTML, and that are linked to tabs that I build in the footer).

So if you’d like to give it a try, visit www.robotwoods.com/dev/web_app_demo from your mobile device. It should work in Android too, the way to install there is to bookmark it, then go to your bookmarks, and long press it, it’ll then give you some sort of Home Screen option. I also put the files in a zip here

Oh, you’ll see in the actual app that the footer has a gradient, I use this site for that: http://www.colorzilla.com/gradient-editor/ I think subtle gradients add a bit of warmth to a UI that may look dull with solid color blocks (see the icons as an example).

And, to minimize the size of your project (which enables faster download), you may want to minify your javascript. I think there are a variety of options for this, I usually just go with: http://jscompress.com/

Lastly, once everything is running as you like it, there’s one more way to improve performance (particularly in those applications that do not require a network connection, for instance, calling a web service for data): cache manifest

By crafting a particular file that outlines all of the files used in your project, the device will (with some limitations) store those files locally, so that it will run even when disconnected from the network. And, as the files are local, it loads much faster as well.

I have covered a number of the important areas of a web app, but there is plenty of other areas and approaches to learn about. What other tips and tricks do you use when building web apps?

JS Parallax Effect

So I have this friend that works in film post-production (you’ve seen his work). We were having dinner awhile back and, as it always does, the conversation turned to tech. We were discussing the recent trend to 3D films, and he explained that no longer are things necessarily filmed in 3D, but rather 3D-ified. His analogy was that the 2D version is a rubber plane, that can be pushed towards or away from the viewer. This causes some distortion, but is much cheaper.

The thing that also stuck with me was that one of the biggest reasons 3D is far from perfect is PARALLAX. This is the effect where when you move, objects far away move much less than those closer to you (hold up your thumb at arms length, aim at something, then notice how much your thumb appears to move relative to that other thing when you tilt your head). Because our heads are always wobbling, we’re using these changes to continually build a 3D map of the scene in front of us, but that can’t be replicated in a film because it’d require some sort of per-person eyeball tracking, converted back to the correct depth calculations, and beamed to the right individual.

But anyway, someone recently asked about how they could replicate this effect on the web. There is a jQuery tool that does this: here. But I wanted to understand it, so decided to build my own.

I started with a scenario like the thumb model. I put up a black background with white speck (space), and then a blue ball in the foreground (earth).

body {
height:1px;
width:1px;
display:block;
background-image:url('space.png');
background-repeat:repeat;
} 
#earth {
position:absolute; 
top:1px;
left:1px;
width:50px;
height:50px;
display:block;
background-image:url('earth.png');
background-repeat:no-repeat;
}

I wanted to have the body be fairly large, so I grabbed the height and width of the window, doubled it, and set those dimensions to the document body like so (body was my css rule 0):

var w = 2*window.innerWidth;
var h = 2*window.innerHeight;
document.styleSheets[0].cssRules[0].style.height=h+'px';
document.styleSheets[0].cssRules[0].style.width=w+'px';

And, I wanted to place the earth center screen, so I calculated that point, placed the planet, and then scrolled accordingly:

document.styleSheets[0].cssRules[1].style.top=((h/2)-25)+'px';
document.styleSheets[0].cssRules[1].style.left=((w/2)-25)+'px';
window.scrollTo(((w-window.innerWidth)/2)+25,((h-window.innerHeight)/2)+25);

Then I needed to add some handling in response to some action. I started with onscroll, but using the arrow keys it felt jerky, and the y-axis seemed to jump more per key press, so I went with mousemove, it was much more fluid:

var x,y;
var earth=document.getElementById('earth');
earth.factor=2;
window.addEventListener('mousemove',pieces);

So now we just need to define the pieces function:

function pieces(e){
	paral(earth,e);	
	x=e.clientX;
	y=e.clientY;
}

Well wait, now I’m just calling yet another function? Why I did this will be evident soon, but here’s the paral function. It determines how far from the last point (x,y) the mouse has moved, and the moves the pieces according to their ‘factor’ (and in the opposite direction, because if you lent left, your thumb went to the right):

function paral(t,ev){
if(ev.clientX>x){//moving right
	t.style.left=(t.offsetLeft-(ev.clientX-x)*t.factor)+'px';
}
if(ev.clientX<x){//moving left
	t.style.left=(t.offsetLeft+(x-ev.clientX)*t.factor)+'px';
}
if(ev.clientY>y){//moving down
	t.style.top=(t.offsetTop-(ev.clientY-y)*t.factor)+'px';
}
if(ev.clientY<y){//moving up
	t.style.top=(t.offsetTop+(y-ev.clientY)*t.factor)+'px';
}
}

So this will allow me to just add items to the pieces function as necessary, rather than needing to add line after line for each element, and each direction. And…it worked! But it wasn’t quite right. Then I realized my error: When I’d made the background image, I had a variety of star sizes (simulating depth) but they were now all moving relative to something else, and doing so in a way that wasn’t natural.

So I then sliced the background into a few different layers (each with ONE size of star), gave each it’s own factor property, and then added each layer to the pieces function…and I threw in another planet for kicks. Oh, and one little housekeeping note, because I wanted the mouse to be generally oriented with the planets, I held off on adding the event listener until the mouse was over the earth, like so:

earth.onmouseover=function(){window.addEventListener('mousemove',pieces);}

And with that, I give you my version of the parallax effect using javascript (click to play, then move your mouse to the earth): Parallax

Can you think of some interesting implementations for this kind of treatment? Do you have any suggestions for improvements?