Skip to: Navigation | Content | Sidebar | Footer

Weblog Entry


March 18, 2008

Just prior to jetting off to Austin last week, I started playing around with a mobile version of this site. While a personal blog is hardly a site that really needs one (unlike, say, an app with a proven mobile user-base like Twitter), I wanted to see what would be involved in re-factoring this design into something more fitting for a mobile environment.

Mezzoblue Mobile

The first step was to create a mobile style sheet. For this I duplicated the CSS file I’ve already built for large screens, and started stripping out the style that doesn’t work so well on a smaller screen. The layout was simplified into a more linear single column, and some elements were re-done to provide a larger target area for a maximum Fitts factor, and background images were dropped wherever possible to cut down on bandwidth demands. I tried building something that would work well on more mobile devices than just the iPhone, but given that it’s my testing device, it works best on that platform for now.

And while I was at it, I thought hey, why not do a TV style sheet too? I’ve got a Wii, it’s got a great browser, and a low-res TV screen could benefit from the same kind of special attention given a mobile device. So I built one of those as well, doing things like increasing font size, increasing border widths, and stretching images to double their original size.

But here’s the thing about media-specific style sheets: the browser in question has to support them. Mobile Safari grabs all screen media style sheets, and ignores the handheld media type entirely. So despite good intentions, my efforts were wasted on it. And that’s what led me down the road of user agent sniffing…

Okay, let’s get this out of the way up front: user agent sniffing sucks. Devices like the Wii and the iPhone have incredibly capable browsers that can render these sites the same as any desktop browser, so it’s reasonable to assume users will want to do so from time to time. (And I suspect that’s why Mobile Safari uses screen in the first place.) Forcing a specific version sucks… if done improperly. But when used well, and not mandatory for the user, I think it’s not entirely evil. And it leads to other potential improvements beyond what CSS can provide, like selective content serving.

The way to sensibly handle sniffing seemed to lie in providing an out: the mobile and TV versions of the site both have a special header on every page that provides a “regular site” toggle link. Any time someone wants to switch back to the regular site, the link is right there in front of them. And all versions now have toggle links in the footer to switch between different media types; given that I’ve seen this on multiple mobile sites, it feels like the site’s footer is resolving into a standard place for where these type of switches ultimately belong.

To make all this work, I had to bust out the PHP. I’ll preface this by saying I’m hardly a proficient coder, so there are bound to be ways to optimize what I’m showing below. I started with a pair of arrays: a list of mobile browser user agent strings, and a second list of TV browsers. The latter is a bit light at the moment, due to my lack of knowledge of what sort of browsers are available for use on TVs. (And that underscores why having a media toggle is useful: if the browser in question isn’t flagged by the sniff, the user can manually invoke the TV version.)

// ==========================
// media check

// array of mobile devices
$userAgentsMobile = array (
  "PlayStation Portable",
  "Windows CE",

// array of tv devices
$userAgentsTv = array (
  "Nintendo Wii",
  "Playstation 3",

Arrays in place, the next step was building a few functions to do things like comparing these arrays with the user’s actual user agent string, and setting cookies to make these media types persist. More on the latter in a second.

// this function takes two arguments: an array of user 
// agents, and a specific user agent.
// it will then try to see if the specific user agent exists 
// within the array. If so, it will return true, otherwise 
// it returns false.
function checkMediaType($uaList, $uaKnown) {
	// check user agent string against array
	// return true if found, or false if not found
	if(in_array($uaKnown, $uaList)) {
		return true;
	} else {
		return false;
// this function takes one argument: a string value that 
// specifies a media profile. It will then set a cookie in 
// the user's browser. It returns the media profile value, 
// to be used as a variable later in the page
function selectMedia($media) {
  setcookie ('media', $media, time()+31536000, '/');
  return $media;

With those functions in place, the code below ended up being the core of my script. In the first major if statement, I’m checking to see whether a cookie is set; the cookie exists to avoid parsing the user agent arrays every single time the site is loaded. I doubt I’m saving that much time if any, given my currently very simple arrays. But I can see them growing over time, so it seems to make sense that this value should persist on the user’s end once the user agent has been determined.

But if no cookie is found, then I’m doing the actual sniffing. I check the user agent string against both the mobile and TV arrays, and then act on them if they match one or the other. If neither matches, I default to the screen version of the site.

// show standard screen version by default
$mediaVersion = "screen";

// toggle media version if cookie is set
if (isset($_COOKIE["media"])) {
  if ($_COOKIE["media"] == "mobile") {
    $mediaVersion = selectMedia("mobile");
  } elseif ($_COOKIE["media"] == "tv") {
    $mediaVersion = selectMedia("tv");
  } elseif ($_COOKIE["media"] == "screen") {
    $mediaVersion = selectMedia("screen");
} else {
  // if no cookie found, sniff media type then set cookie
  $knownUserAgent = false;
  // compare the device arrays against the 
  // client's user agent
  $mediaTypeMobile = 
    checkMediaType($userAgentsMobile, $_SERVER['HTTP_USER_AGENT']);
  $mediaTypeTV = 
    checkMediaType($userAgentsTv, $_SERVER['HTTP_USER_AGENT']);

  // if media version is found, set a media type cookie
  // otherwise flag this browser as screen to save 
  // time on future loads
  if ($mediaTypeMobile) {
    $mediaVersion = selectMedia("mobile");
  } elseif ($mediaTypeTV) {
    $mediaVersion = selectMedia("tv");
  } else {
    $mediaVersion = selectMedia("screen");

And then the last step is checking to see whether any of the media toggle links have been selected. If a user is viewing the mobile site and wants to switch over to the regular site, I need to re-set the cookie so their new preference persists. This is all done through simple query strings; the HTTP GET variable being checked for (media) can be invoked simply by adding ?media=mobile to the end of a URL or link.

// override media version and set a new cookie 
// if they have selected a "show {media} site" link
if ($_GET["media"]) {
  if ($_GET["media"] == "mobile") {
    $mediaVersion = selectMedia("mobile");
  } elseif ($_GET["media"] == "tv") {
    $mediaVersion = selectMedia("tv");
  } elseif ($_GET["media"] == "screen") {
    $mediaVersion = selectMedia("screen");

Now that the cookie exists and the $mediaVersion variable has a value, I’m all set. Anywhere in the rest of my site, I can use a simple if statement to filter out the specific media version I’m targetting, and selectively show or hide content for it. This is how I’m hiding my header photo from the mobile version, for example (simplified for clarity):

  // don't serve this up if we're talking mobile
  if ($mediaVersion == "screen" || $mediaVersion == "tv")  {
  <div id="header-photo">
  <img src="/i/photos/<?php 
    echo $currentPhoto["largephoto"]?>"
    width="505" height="243" 
    alt="<?php echo $currentPhoto["description"]?>" />

I put together the entire set of functions and a couple of small demonstrations of selective content into a file you can grab. (live demo)

And to finish, a couple of highlights from the alternate media versions of this site. The mobile version goes small by stripping out images where appropriate to save on download times; the header photo is gone, avatars on comment pages are gone, and most of the decorative PNGs have been removed or converted to CSS border properties. While the TV version goes big by doubling font size, increasing border widths, stretching the header image to fill the entire horizontal screen width, doubles avatar sizes, and using a higher-resolution version of the site’s logo.

Quick caveat: I’ve only had the opportunity to test the alternate media styles on a limited range of devices, and likely won’t any time soon. Device testing is too hard if you don’t already have access to a wide range of devices. I tried stripping out some of the more complex CSS tricks like overflow clearing and absolute positioning, so even my Treo doesn’t make a horrible mess out of the site, but no guarantees.

And the bonus question: why am I serving up the media versions with media profiles set to all? Simple reason: clicking through and viewing the TV-only version on a computer was seriously ugly, because it ignored the style sheet. As it should. But people will click through anyway, so that was problematic. Simple fix: media="all"

Updated to include iPod Touch.

Updated PHP for minor optimizations in response to comments.

March 18, 13h

I’ve been writing iPhone specific styles in most of my work over recent months, mainly to control text resizing, and to avoid needing any server-side malarkey, I’ve been using:

<link media=”only screen and (max-device-width: 480px)” href=”iphone.css” type=”text/css” rel=”stylesheet” />

Maybe this will help some?

Dave S. says:
March 18, 14h

It does, some. It gets around that ignoring handheld media problem that mobile Safari has.

But if you want to selectively serve/hide content (ie. completely avoid downloading a large image that’s display:none hidden from the document) then content re-factoring’s your best bet.

(That said, I can’t say with certainty that mobile Safari doesn’t in fact download display:none image. That also said, mobile Safari is far from the only mobile browser out there.)

March 18, 14h

Oof, very nice indeed, thanks for this :) Finally makes design for small screen devices practicable.

March 18, 14h

Thanks! Very informative.

However, when I pulled it up on the iPhone, I still had to zoom in to the content area, I think it’s due to the larger image in the content area of this post - wonder if there’s a way to easily scale that down too. I actually preferred the regular site to the mobile version anyway. But that would likely change if I were browsing on Edge =D

Val says:
March 18, 14h

A tiny optimization, but you mentioned you wanted to be a bit forward-looking:

In your function checkMediaType(), you iterate thru the entire array even after you found a match. Unless you want to match against multiple values (and I don’t think that’s the case; the userAgent you’re testing should only match zero or 1 entries in the array, yes?) you should escape the loop as soon as you have a match:

function checkMediaType($uaList, $uaKnown) {
foreach ( $uaList as $userAgents ) {
if( strstr($uaKnown, $userAgents) ) {
return true;
return false;

or, avoid the loop altogether:
if ( in_array($uaKnown, $userAgents) ) {
return true
return false;

Unless I seriously misunderstand, in which case, never mind :-)

Neal G says:
March 18, 15h

Dave it looks great! The only change I would make is to get rid of the fixed with and just set everything to 100% since it’s difficult to tell how wide a mobile device is. I like your idea of showing the accessibility links at the top too.

Dave S. says:
March 18, 15h

@David Joyce - “However, when I pulled it up on the iPhone, I still had to zoom in to the content area”

Yeah, ironically due to using content types I haven’t been doing much with lately, the initial draft of this post did odd things to the alternate layouts. A bit of CSS work later, and try refreshing. Should be all better!

@Val - yeah, in_array would suit my purpose better. Thanks, good idea!

March 18, 15h

Nice work, Dave! One thing that’s kind of ironic is that the real meat of your first mobile-enabled post is code that can’t be read on the mobile version of the site (due to what looks like overflow:hidden). Of course, there’s not much anyone could do with all that code while they’re on the go, but I thought it was funny nonetheless.

Dave S. says:
March 18, 15h

@Rob - If only mobile Safari did something useful with overflow: auto, I could have my cake and eat it too. That’s the unfortunate result of fixing the prior-mentioned initial layout problem with the code.

Yay, compromise.

Craig Burnett says:
March 18, 15h

I follow the same path as Andy C., especially as the latest/future versions of Opera Mini & Mobile are following the same path as Safari in using screen as a default, not handheld.

Mo says:
March 18, 15h

I’ve found that mobile versions of desktop browsers tend to behave far more like desktop browsers, and thus ignore the handheld stylesheets.

On the flipside, browsers which are clearly not really up to par with their desktop bretheren seem more suited with handheld or mobile media types.

Personally, I’d tend to make a “handheld” or “mobile” stylesheet much lighter on fluff than a “fully-fledged style, but tweaked for a smaller screen size”; people browsing with iPod touches (which your UA sniff will fail to get, incidentally) and iPhones aren’t geared to expect a “light” version, but possibly will benefit from a “doesn’t need zooming” version (whether the two are the same thing is a design decision, of course).

March 18, 17h

I would include “Aspen Simulator” under mobile as well to pick up the iPhone simulator that’s included in the iPhone SDK.

March 18, 17h

It looks pretty nice using the Treo’s Blazer browser in optimized mode. The header-content section needs some work. Recent Entries is a very light gray and everything else is right justified. I get a horizontal scroll bar for every image and every pre section. However I don’t have to use the horizontal scroll bar to view the images or pre sections.

March 18, 19h

While some of this is beyond me, it’s interesting to see how you worked it out and blazed forward.

PHP is like an invisible golden egg to me. I reach for it but never get it. I’ve had a little fun with it, but can’t seem to wrap my head around arrays and functions enough to conjure one up.

These types of posts interest me! Thank you!

Veejay says:
March 18, 21h

Adobe’s device central is a very helpful tool in testing for various devices.
Nice article, by the way.

Jon says:
March 18, 22h

@Rob “One thing that’s kind of ironic is that the real meat of your first mobile-enabled post is code that can’t be read on the mobile version of the site”

You can pan the code on an iPhone by using 2 fingers - just like scrolling textareas :)

March 19, 02h

Why reinvent the whell ? :)
You can use WURFL, it’s a database of devices with their capabilities… No need for your own code when there is a much more advanced one that already does it all in a tested way :)

URL of WURFL : (there is a php api for it)

dryan says:
March 19, 02h

I whipped up a quick function to tell if an element is overflowed. Currently it just appends a class to the element, but it wouldn’t be too hard to work this into a function that added scrolling buttons below and/or beside an element that needs scrolling on the iPhone to make it more obvious that the element can, in fact, be scrolled.

function checkOverflow(elems) {
    for(var i = 0; i < elems.length; i++) {
        var elem = elems[i]
        if(elem.clientWidth < elem.scrollWidth) {
            elem.className = elem.className + ’ horizontal-scroll’
        if(elem.clientHeight < elem.scrollHeight) {
            elem.className = elem.className + ’ vertical-scroll’

I think I might work this into a plugin for jQuery so you could call $(‘pre:overflowed’) to get a collection to work on.

@Tanny: showing the scrollbars is Blazer’s default behaviour for overflow: auto. The CSS spec allows browser developers to decide how to handle the auto rule, several of them (mostly older codebases) treat it by always showing the scrollbars.

dryan says:
March 19, 02h

After all that, I forgot why I originally planned to leave a comment: print style sheets. I’m a front-end engineer by trade and try to make a print style sheet for each site I do. Typically I’ll cut out menus and such, add a print-quality logo, and convert font sizes and border widths to points as these tend to print much cleaner. On most of the sites I work on, these steps cut the number of pages needed to print any given page on the site by 1/3 to 1/2.

Erlingur says:
March 19, 02h

Only two things regarding the PHP code (besides what Val said above):

“alt=”<?php echo $currentPhoto[“description”]?>” />” could be shortened to just “alt=”<?=$currentPhoto[“description”]?>” />”

and I belive that using $HTTP_COOKIE_VARS has been deprecated (use $_COOKIE instead).

Other than that, great work as always!

March 19, 05h

Great timing on the post. I’ve been playing with these too for a site I am working on, and I’m pretty disappointed that we have to resort to browser sniffing.

And, the more I thought about it, the dumber the decision to make a “screen” and “handheld” and “tv” media types became. Do handhelds not have screens? Are TVs anything but a screen?

As I started looking into media queries, I was excited – a perfect way to specify a layout and design based on device capability instead of an ambiguous term. Two minutes of testing – a Win CE device, FireFox, Safari and Safari Touch – and it was clear support for media queries was going to add yet another wrinkle to the world of CSS incompatibilities.

So, browser sniffing it is. For that, this is a great jumping point!

Justin says:
March 19, 05h

@Erlingur Yeah he could do that but only if short_open_tag is enabled.

I know short tags can conflict with XML (<? vs. <?php), but does the jsp-style echo shorthand have the same problem? I’d assume the “=” makes all the difference with XML collisions, but I have never heard definitively.

March 19, 07h

@Justin All the php tags are parsed on the server before being sent to the client. So the browser should never see any of the <? style symbols to confuse them with XML. It might still cause problems in web page editors I suppose.

Beau says:
March 19, 09h

You should be able to scroll elements on the iPhone with overflow:auto using two fingers, so no need to but overflow:hidden.

Beau says:
March 19, 09h

In fact, I just tested your iPhone version and I can scroll your code blocks using two fingers already.

March 19, 09h

It’s nice to see some sort of consensus emerging about where these links should sit. A few months ago I couldn’t find enough mobile sites that had them to be able to spot any kind of similarities. :)

I wrote a plugin for people who use the symfony framework to do this sort of browser sniffing and platform detection for mobiles, which some might find useful. Obviously the downside is that it does rather assume you’re using symfony!

David says:
March 19, 10h

@Beau Great tip on the two-finger scroll! I had no idea.

@Dave S. VERY informative and well done. This post came a the perfect moment for me. Thanks!

Justin says:
March 19, 10h

@Mark Fischer – Yes, but most people (even discourage against using short tags mainly because of portability issues (not all servers allow use of short tags) and because it can trip up the parser if there’s embedded xml in the php file.

I know I’ve moved code between servers that didn’t allow short tags and had to refactor a lot of <?=s just because of that. We’re kind of splitting hairs at this point though.

Very nice post Dave–really great ideas that I’ll likely put to good use soon.

Dave S. says:
March 19, 11h

@Val - thanks again for the suggested loop optimization, I’ve updated the code in the post/demo accordingly.

@Erlingur - thanks for pointing out $_COOKIE, I’ve updated the code in response.

@Justin - thanks for explaining the short tag thing better than I would have.

March 20, 05h

A mobile stylesheet with display:none on stripped elements isn’t enough. You should also present alternative mobile source code. Why? because nobody wants to download 150k data that isn’t displayed anyway.

Mobile connections are often much slower than regular broadband (T1, Cable etc). A mobile stylesheet is great, but your site will be very slow unless you also present a stripped HTML source for mobile browsers.

March 20, 05h

Very interesting article. I poked around converting pages with small modifications to my existing page for a while.

There is one useful addition to your , so Google knows about the mobile part of your site (if it has a separate URL):

<link rel=”alternate” media=”handheld” type=”application/xhtml+xml” href=”” />

If someone uses the Google mobile search, he will be automatically redirected to this URL.

March 20, 05h

BTW: Your function checkMediaType could be written even shorter. :)

function checkMediaType($uaList, $uaKnown) {
// check user agent string against array
// return true if found, or false if not found
return(in_array($uaKnown, $uaList));

Anil Dash says:
March 20, 14h

Moving the conversation up a level from the PHP code-fixes… what about what we’ve learned about the inadequacies of media types in CSS? Screen? Mobile? TV? Those aren’t useful distinctions. It’s another classic example of spec failure due to inadequate prognostication abilities. Someday we’ll have the ability to see the future and this will all be easy.

What actually distinguishes these different media? Screen resolution? To some degree, the utility of form elements. (Different devices make them more or less practical.) Ability to display or not display non-textual items. Scripting support. It seems like profiles of capabilities could be captured, in the spirit of progressive enhancement, and described as media for this purpose. Maybe even mapped to the existing media types, but more appropriately used to define new media types. And then a user agent creator wouldn’t have to say “well, I gotta use screen – I want all the features!” Instead, they could design to a use case.

Am I being a pollyanna?

Dave S. says:
March 20, 23h

@Anil - it’s really too bad we were speaking at the same time at WDN, this is pretty much exactly the territory that John Allsopp and I covered:

We dug into the design patterns and UI elements that break down when you take away basics like a functional keyboard, and covered situational context and screen size alike.

More questions than answers, I’ll admit, as we’re all just starting to turn our sights on these issues. This whole post is just a drop in a very large bucket; we’re still on the left side of the curve right now.

MattH says:
March 21, 07h

Very cool idea. I think a purely client-side analogue of this might be something like Switchy McLayout:

March 22, 21h

I like how you add the link to switch versions. Far too many mobile optimised sites don’t give this option, and the optimised site is often not what the user wants.

For TV you could deliver a tv media type. The Wii supports this and any TV based browser from Opera in the future will also likely support it. Using the media type would future proof the site as you don’t have to follow what new browsers come out. As the Wii is popular and quite a few sites optimise for the TV experience (sometimes lower res, but mostly longer viewing distance) on Wii, and so it would make sense for other TV browser manufacturers to support TV.

For mobile on the other hand, we’ve removed support for handheld media type, except when in single column mode (mobile view). I think this is unfortunate, but there were sites that were abusing handheld stylesheets, without testing them properly, which would break our mobile browsers on those sites. As iPhone didn’t support handheld, it made Opera worse on those sites, and thus we switched behavior.

Media Queries would be ideal, however due to bugs in certain browsers (such as IE and pre Safari 3) you can’t really use media queries to deliver separate stylesheets. If you do that, those browsers will show no styles at all. Instead you have to override the original stylesheet i the mobile stylesheet, so that it loads both stylesheets on smaller screens. This isn’t exactly ideal. I’d also advise against using media queries to deliver iPhone optimised stylesheets, as more browsers support media queries, and they don’t all have the same screen size, standards support or bugs as the iPhone. Designing for iPhone is scarily similar to the old days of just designing for IE (except worse as it is just one device, not just one browser).

March 24, 18h

@Dave: Cool article. And indeed challenging to create design that will be appealing for eyes with different capabilities and forms factors.

@Anil: Devices Capabilities profiles have been discussed for a long time. Quite challenging to live in a world where there is a lot of diversity. Ubiquitous computing is faced to these issues not only for rendering but also for network and protocols capabilities, printers avaibilities, etc.

On the CSS side, Media queries is a W3C specification which has evolved and tries to specify requirements for screen sizes for example. That will not answer everything, and more challenges will rise.
Media Queries:

CC/PP 1.0 was created for expressing devices capabilities, but it has not been used and deployed on browsers. There is also a work going on by Ubiquitous Web Applications Working Groups about Delivery Context and new versions of CCPP. The main issue as always is that it will not be necessary taken into account by one part of the market. The main participants are device makers.

CC/PP 2.0:
Article explaining CC/PP:

Embracing diversity and specifying precisely things at the same time is quite a challenge. And the designers at the end of the (business) food chain pay often the price. Unfortunately.

March 31, 14h

I’ve been working on something very similar to this except I use my own get_browser ( static class that uses SQLite to store the information because Gary Keith (the guy maintaining the browser lists) is sick of the function’s bugs to actively maintain the PHP specific version. After a friend and I programmed it it turns out using the database is about twice as fast as the original. I use it to determine what browser the person is using and feed the stylesheet accordingly. I was just going to feed them that, but your idea with displaying a menu giving people a choice is quite nice.

I’m unsure if this is intended or not, but to view what you created for myself I had to go to the footer of the document to click on a link to view the mobile version. I tested it both on the iPhone and using Opera’s demo of Opera Mini (

Christian says:
April 08, 17h

Just tested your site with the Nokia N95. First thing to mention is it doesn’t detect it, but the code above with the in_array() can’t work anyway, due you want to find array-elements inside one string (actually, with the code above you should get an error because in_array doesn’t even accept a string as second parameter).

You’ll need a loop here, this will work and is resource-friendly:

for ($i=0, $pos=false; ($pos === false) && isset($uaList[$i]); $pos = stripos($uaKnown, $uaList[$i++]));

return ( $pos !== false );

Talking the look the mobile version is pretty good, just the image in the post exceeds the screenwidth on the N95. I’d suggest 3 solutions:

a CSS-based max-width:100% (which doesn’t work that great, some browsers will constrain proportions, others won’t)

automatic conversion of all images to smaller versions (utilizing PHP)

stripping the images (if they’re not relevant)

Nicholas says:
April 09, 02h

Hi Dave still loving your site, just a thought but have you tried wrapping all these php scripts up into a object, it would be alot more portable and usable and would also allow you to build a profile of the users viewing platform/browser and send the relevant styles back from the server, just a thought….

Allan W. says:
April 20, 15h

Great article, perfect timing. I’m building some blogs for people and want to offer mobile versions.

My current thinking is that I’ll let them switch, or hit a different URL for a mobile version. I’m going to serve up simplified templates (PHP/expression engine) and CSS that makes links easy to hit. It’ll be iPhone-centric at first, because that’s what we use for ourselves. I want to make sure it’s full-featured enough, though, and that’s going to be the challenge.

mb says:
June 05, 07h

Every time I’ve read an article about mobile web browsers it’s a flashback to the bad old days of the browser wars.

Instead of coming up with these kind of workarounds, clever though they may be, wouldn’t it be better for everyone if we pushed browser makers to implement support for appropriate web standards?

Like your experience with Mobile Safari ignoring the HANDHELD media type. That’s a foolish decision by Apple. What else is it but a handheld browser? By all means include the CSS switcher you mention, but don’t just ignore the media type by default.

russell says:
November 08, 23h

I made a small change to check for OperaMini, it seems to shift the UA to a separate thing:

$mediaTypeMobile = checkMediaType($userAgentsMobile, $_SERVER[‘HTTP_USER_AGENT’]);
if(!$mediaTypeMobile && (isset($_SERVER[‘X-OperaMini-Features’]) || isset($_SERVER[‘X-OperaMini-Phone-UA’]))) $mediaTypeMobile = true;

don says:
November 16, 16h

you can also use one of the web services out there such as which streamlines the process for us.