Saturday, October 16, 2010

Practical CSS3 Media Queries and Mobile Browsers

tl;dr: Jump to the end of this post ↓

I've been working on-and-off on a little project on the side, and have been trying to make it as buzzword-compliant as possible, because, you know, that's what all the cool kids are doing. Making it buzzword compliant meant that I had to use persistant connections to the server for real-time data exchange, use non-blocking I/O, I just had to use node.js, and throw in generous portions of NoSQL. So I did all of that, and now my server is worthy of a new starburst sticker.

Now that the server was mostly ready, I shifted attention to the client. Clearly, I had to use HTML5 and CSS3, think heavily about Web Performance Optimization, and ensure that the usability trolls don't get a chance to make me look bad. That was the easy part, since I have been doing these things for years. The fun started when I decided to flirt with an idea I had never played with before.

The Mobile Web

Mobile devices, it seems, have finally arrived. We have forever dreamt of an always-connected world, and mobile devices seem to be delivering on that dream. Indeed, we are on the verge of having more traffic from mobile devices to the web than from desktop browsers[citation needed]. In such a scenario, it's impossible to ignore mobile devices.

The problem is that no one knows what the mobile web means. In such a case, it's absolutely fine to interpret it the way you want to, and that's what I did. So, here's what the mobile web means to me.

The mobile web is that part of the web that works well on mobile devices.

Pure genius of a definition, isn't it? I know. Thank you. There's an ambiguity in the definition there though — what does it mean to work well? Turns out, no one knows that either. But there are some hints:

  • It should work fine in the mobile browser — no native apps, no downloads.
  • Context is king. The site should provide the right amount of data that is needed in a mobile user's use-case at that time.
  • The site should be touch friendly, meaning take care about hit areas and stuff because some people have fat fingers. Not me — my fingers are beautiful. Just thought you'd like to know.
  • Lightweight and fast. Because I want my data quickly, and I'm paying by the byte. This is true of the desktop as well, but especially true of the mobile web, because network operators try to suck as much as possible out of the wallet by giving as little as possible.

So, how do we go about building such a site? Generally speaking, this necessitates building a separate version of the site just for mobile browsers. Trust me, this effort is worth it. Don't believe all those standardistas who say change your CSS file, and everything will be magically fine. Their latest mantra is use CSS3 media queries and everything will be magically fine. Like most other things in life, nothing is that simple.

But there are times when that actually works. And this post is about those times. This post is only useful if:

  • Your site is already light enough and tightly focussed enough that the markup for your desktop version is going to be the same as that of your mobile version.
  • There's not really too much contextual data you need to provide to your mobile users, so again the markup is the same.
  • Your site is styled primarily with CSS. You are doing that already, aren't you?
  • You are lazy, and don't want to create multiple versions of the same content, otherwise known as maintainability or code reuse.

I'm definitely lazy, and I'm proud of it. I'd rather not write any code at all. If I have to write any code whatsoever, I'd rather write it such that (a) I never have to look at it again, (b) I keep using the same thing over and over again so I don't write the same thing ever again, and (c) I should write as little as possible. Fortunately, these are all considered good practices — maintainability, reuse and conciseness. I bet whoever said these were good practices was lazy. But as long as the rest of the world doesn't find out, this can be our little secret.

There's a problem with being lazy though. You have to work hard to be lazy. How ironic is that! And hard work is, well, hard. So, there's where I was, wanting to be lazy, tinkering with the idea of the mobile web, and looking at CSS3 media queries from the corner of my eye. Being lazy, before I wrote a single line of code, I wanted to know everything about challenges for mobile phones and media queries. I'll cut a long story short, and tell you my findings.

  • There's no standard screen size for the mobile. If you plot a graph of mobile screen sizes, the graph is going to be all over the place. That basically means you have to design for all screen sizes! This is going to be fun. Not.
  • Media queries let you target styles to different ranges of screen sizes. Awesome. Seems like a right fit for the job. So, we can have a different CSS file targeted to small screens, a different ones for medium sized screens (like the iPad), and the full of crap ones for desktop browsers. Neat. Except...
  • CSS3 media queries don't work in most mobile browsers and IE! Bam! Just when we thought we had a solution. Well, to be fair, it does work in some mobile phones — the iPhone's and Android's WebKit browsers being good examples, but both these browsers put together are such a small percentage of mobile users, it's not even funny. Blackberry's horrid browser, Opera Mobile and Symbian's native browser have the largest market share of all mobile browsers. We can't just ignore them, can we! Just like we reluctantly show love to IE, we'll have to show some love to these browsers as well. It's that kind of a relationship. (BTW, I think Opera Mini does support CSS3 media queries. I haven't tested this.)

So, I was going to throw the idea of media queries completely out the door, you know, baby and bath water and all that. This is when I saw an excellent talk by Luke Wroblewski. What he is advocating is that we should build for mobile sites first, and for desktops later. His arguments are mostly non-technical and about usability, design and innovation, but he underscores the importance of designing for the mobile. I get that, but it doesn't help me be lazy. Well, it could, but I didn't know how. It still looked like double the effort. Except, he had planted an idea in my head. You know, much like Inception. Come to think of it, Luke does look a little bit like DiCaprio, doesn't he? Cool!

Mobile First

So far, I was thinking of making separate CSS files for mobile and desktop, and doing so in parallel. That mostly meant I'd do the desktop version first, and the mobile version next. Then I'd target different screen sizes with media queries (which I discovered wouldn't fly) and voila it would work everywhere. Instead, Luke's talk got me thinking: what if I design for the mobile first, and then target desktop browsers with media queries. This would theoretically work, since most mobile browsers are old, and they get the vanilla CSS files. For newer mobile browsers and most desktop browsers since they are new as well, I'd serve the full css file targeting them with media queries. Newer mobile browsers will understand it's not for them, so they'll ignore it. Older mobile browsers won't understand it at all, and hence ignore it. Most desktop browsers would know that the CSS file is for them, and hence apply it. This leaves our old nemesis Internet Explorer, but we're used to that already. We'll solve that problem later.

Now, about actually pulling it off. I found this document at Dev.Opera that goes into a fair amount of detail about media query parsing differences between browsers, old and new. This doc would definitely help. Now, I needed to figure out how to structure the CSS. Remember my laziness? This meant that there should be as little repetition between the mobile version and the desktop version. What would generally be the things that remain common across the two types of browsers? There's actually quite a bit. There's color schemes, there's some type information, and possibly some CSS niceties like rounded corners, gradients and effects. What's going to be different? It's mostly going to be font-sizes and layout. Sounds good so far, let's start implementing it.

The code

Since we've decided to work only with mobile browsers first, and serve it CSS like it's the 1990s, the following markup should do it.

<link rel="stylesheet" type="text/css" href="narrow.css" />

I actually went ahead with this and built the whole mobile version of the site, and tested it in Mobile Webkit on Android and iPhone and in Opera Mini, which are the two browsers I had access to on a couple of devices. I kept the layout fluid, since I didn't know the screen size before hand, and didn't want the site to break in screen sizes I didn't have control over. I strongly recommend that you go with fluid width layouts for narrow screens as well. I even tried it in a couple of emulators and it all looked fine. Of course, some of the fancy CSS rules didn't work on some browsers, but that was ok — things still looked reasonably pretty and it looked mobile-web-optimized. Awesome. Now, I turned my attention to the desktop. This was when the Dev.Opera doc came in handy. This is what my markup looked like:

<link rel="stylesheet" type="text/css" href="narrow.css" />
<link rel="stylesheet" type="text/css" media="all and (min-width: 400px)" href="wide.css" />

See that media attribute there that says media="all and (min-width: 400px)"? That's the media query. It means target this CSS to all media that have a min-width of 400px. Why all media? Because I was being lazy and didn't want to work on a print version at the moment. Also, it seemed like the most reliable media type to target from Opera's documentation. Why base this on min-width? Because that's the best way to target different device sizes. Why 400px? Because in portrait mode it would apply the narrow.css (screen widths are generally 320px for devices that support orientation), but in landscape mode or for most desktop browsers, it would apply the wide.css file. This suited me, since I wanted to utilize the extra width in landscape mode better. YMMV, and you should play with this number to suit your tastes.

Also, since I was applying both the files and hence cascading their rules, I could already go with most of the stuff defined in narrow.css. wide.css only had to override some properties — mostly font sizes, block positioning and layout. Tested this, and it continued to work with all the mobile devices I was testing in as it was before, and worked on desktop browsers with the wide.css layout. I just had to add a few extra divs some HTML5 semantic markup to get some extra styling hooks. Success! And I'm continuing to be lazy since I'm reusing as much code as possible. There is a disadvantage to this technique though. It requires the download of two files. I could have combined them into one file, but that would've introduced some hacks as described in the Dev.Opera document. I am consoling myself by saying that wide.css is actually a very small file, and CSS files are downloaded in parallel anyway, so the impact shouldn't be huge. But that said, it is something to think about.

Wrapping it up

That just leaves us with one beast — the old nemesis — Internet Explorer. We've gone through all this trouble to support all sorts of crappy old mobile browsers, it only makes sense that we don't leave IE behind. Now, IE doesn't understand media queries, so how do we serve it wide.css? Turns out, we can use IE against IE, thanks to it's own conditional comments! The final markup looked like this:

<link rel="stylesheet" type="text/css" href="narrow.css" />
<!--[if lt IE 9 ]>
<link rel="stylesheet" type="text/css" href="wide.css">
<![endif]-->
<!--[if (gte IE 9)|!(IE)]><!-->
<link rel="stylesheet" type="text/css" media="all and (min-width: 400px)" href="wide.css">
<!--<![endif]-->

Let's dissect that. Firstly, serve the old narrow.css the way it was being served. No change there. Next, detect if the browser is IE and the version is less than 9. Why 9? Because MS is promising that IE9 has got media query support, so we'd rather use that over blindly serving the file to IE. If it is less than IE9, serve it wide.css no questions asked. If the IE version is 9 or greater, or if it's a non-IE browser, serve it wide.css with the media query.

This post is bound to be outdated before it's even out, since MS is launching Windows Mobile 7 or whatever it is called with a very uninteresting version of IE which is purportedly somewhere between 7 and 8. But I don't know yet how the browser is identifying itself, and hence can't exclude wide.css from it. If you find out, please leave me a comment, and I'll update my post with your findings.

In the end

We've successfully managed to:

  • Target mobile phones, new and old.
  • Match device orientation for newer phones.
  • Apply different CSS to desktop browsers.
  • Get it working even for the thing that is IE.
  • Keep Luke happy.
  • Done all of this without using any pesky JavaScript or server-side UA sniffing.
  • Continued to be lazy.

This has not been without drawbacks:

  • Two CSS files are downloaded to wide clients. To make things worse, some parts of narrow.css are overridden in wide.css, making the double-download even more painful.
  • Mobile IE gets the wide version, mostly because I don't know too much about it.

This works for me for the moment though. Would love to hear your thoughts.


Like this post? I blabber on a lot about this kind of stuff on the twitters. Follow me there if you like.

Sunday, April 11, 2010

Section 3.3.1 of iPhone SDK License Agreement

It has been an interesting week for iPhone developers. iPhone just released the developer preview of the iPhone OS 4, which has got features that are exciting to both developers and users. However, John Gruber discovered that Apple made a change to their iPhone SDK Licence Agreement with this release.

3.3.1 — Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs. Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).

This amendment has far-reaching effects. The effects are so powerful that the FUD surrounding it over-shadowed the excitement of the OS upgrade itself. There's no doubt that there's evilness in this amendment to the ToS. The question is what part of it is evil, and how should we react to it. Let's analyze this amendment, and see what it potentially means.

On APIs

Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs.

This seems reasonable to me. It's Apple's platform, and they expose a bunch of APIs, and they only want developers to use those APIs. However, it stinks of potential anti-trust cases. However, this is not what got people pissed off for, so let's skip past this statement.

On language of choice

Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs

This is plain stupid, and only serves to push developers away. Everyone knows that the choice of programming language hardly matters in any meaningful way, and what the developer is comfortable with is what s/he should ultimately use. This clause doesn't allow for that, and there's no good reason why it shouldn't. Just plain stupid.

This kills the soon-to-be-released CS5 Flash-to-iPhone compiler, the MonoTouch project which compiles C# and .Net to the iPhone, and the Unity3D project that let you program 3D games in C#.

However, also consider that without asking you for your source code, how can Apple guess which programming language you used for development? It's all compiled anyway. It just doesn't make sense. I don't see how Apple can enforce this. We'll have to see how this develops.

On compatibility layers

(e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).

This is the big one, and is the reason most people are pissed off, because it's implications are far reaching.

Firstly, consider the case where I want to build an iPhone app using Ruby, and I use a library that has wrappers on top of the Objective-C APIs. This is like the point above, where I should be able to choose my language and program in it. And like I said above, there's no reason to restrict this, and it's stupid on Apple's part to do this.

The other case is that of compatibility layers, where I code to an API much like above, but the compiler is designed to cross-compile to multiple platforms, not just the iPhone. This is the big one, and is what makes the future of platforms like Titanium and PhoneGap unclear. This means that as a developer, I only need to write once and the environment let's me compile and run anywhere. As we all know the write-once-run-anywhere dream has eluded us all the time — Java, Air and Flash being great examples of it. A look at applications like Eclipse, Open Office, etc., and virtually anything developed using Flash, and you'll see that it feels nothing like a native application on all platforms. This is neither the fault of the language nor the compiler, but that of the platform APIs (like Swing in Java's case). The problem is that since the platform API, which serves as a compatibility layer in these cases, tries to expose APIs that can work everywhere, they are forced to do a bad job of it.

Let me illustrate this with an example, to drive the point home. The Mac doesn't have a UI for combo-boxes, whereas both Windows and Linux do. What would a compatibility layer do in such a case? There are four possibilities:

  1. If the API makes it possible to use combo-boxes everywhere, it'll not feel native on the Mac, because the OS itself doesn't do these kinds of UIs. The platform will have to fake it.
  2. If it doesn't let you use combo-boxes anywhere, it'll seem less powerful on Windows for developers, and will provide a bad UX for users.
  3. It can choose to present completely custom UIs, which falls in neither camps, and hence fails.
  4. They could also say that you can use combo-boxes in Windows and not on the Mac, but that'll only make the programming experience horrible for developers.

So which approach is the least bad? Swing decides to compromise on UX. Flash goes the route of custom UIs.

An interesting case to consider is that of Google Chrome, which has done the absolute right thing. The core engine is C/C++, and UIs are coded separately for different OSs. However, this is clearly expensive to do, and only someone with Google's resources can pull it off.

Of course, there's also the possibility that Apple just wants to make it hard for developers to build apps that can run anywhere, by creating a lock-in. However, I don't think this is the case, because they are still allowing JS to run, and their browser is one of the best browsers ever that a developer can work with. This browser itself can be looked at as a compatibility layer that works across platforms. I've actually tried it — fairly complex applications built for the iPhone work just fine in Android-based touch phones with zero effort to port it. This is because when you develop for the iPhone browser, you are actually developing for Mobile WebKit, and has nothing to do with the iPhone Objective-C APIs per-se.

Aside: I've just negated my own point. I've criticized cross-compilers, and then said that the browser is a great environment to code because you are platform independent. However, I think that you should think different (pun intended) about browsers. This is because the browser is a sandbox that runs the Internet from the user's perspective, and it's expected that the UIs on web pages will be platform independent. That's not the case with native applications. Besides, the browser doesn't just run applications — they are also content distribution channels.

From the point of view of the UX of applications built using a cross-compiler compatibility layer, I sympathize with what Apple's done. Do I think such apps should be banned completely? I'm sitting on the fence on this one. I think the best thing to do would be to let the application fail in the market, rather than disallowing such applications outright. But then, I can see why Apple would ban it as well. Apple has always been a stickler for the UX of applications on their platforms. On the other hand however, they enforced this on the Mac OS by giving guidelines, not by disallowing such applications. So this thought is full of contradictions.

Then there's the case of the app store. The idea of a market place is awesome, but Apple's moderation is just stupid. Other issues apart (and there are several), it's a model that simply doesn't scale. Maybe Apple did this 3.3.1 thing just to ensure that they can moderate lesser apps and make their moderation generally easier to do? If that's the case, Apple is just reducing their problems and moving it to all their developers. Just another reason why Apple's moderation is stupid. Why Apple moderates is beyond me. No other platform that I know of does this, including Apple's own desktop OSs.

History repeats itself, sort of

There's a bigger concern I've got. Let me put it this way. I used to be a VB developer back in the day. When the Internet started becoming interesting, I found out that I could take my VB knowledge and port it to the web to build ASP programs. It was awesome, and I loved how Microsoft had made it easy for devs to move to the web as a platform. However, when the .Net framework was released, MS told developers like me that ASP was not cool anymore, and that we should choose either C# or VB.Net. VB.Net, for those who don't know, is nothing like VB. It's an entirely new language. Besides, MS seemed to put it's weight behind C#. Most developers were convinced that they should move to C#.

Not me. I was filled with FUD. I couldn't let a company decide what programming language I should use. What if I pick up C# and a couple of years later, just as I'm getting comfortable with it, they tell me that C# is not cool anymore? Am I shooting a moving target?

That was the time when I picked up my bags, and moved from the MS platform to open platforms. I picked up PHP because it's programming model resembled ASP, so the learning curve would be smooth. Of course, I still had to learn a new language, but at least this time it wasn't controlled by a company and I could use it just because there was community support. And PHP had (and still has) great community support. I had immunized myself from what companies have to say about my development tool chain. I was free!

In hindsight, this was the single most important decision I've taken that transformed my career. Not that I was any good at PHP. But the world of open platforms and open standards based development made me a better programmer.

Though the situation is not exactly the same in Apple's case, it's not too different. Here's a company screwing it's developers up because of what they think is the ideal vision for the platform. It's not a democratic decision — neither MS nor Apple consulted me when they were making this change. If I was now an Apple developer, I would hear alarm bells ringing, and would refuse to work in such a totalitarian environment.

But the iPhone is where my target demographic is!

Sure. I still think despite all of this that the iPhone is still a cool gadget, and users will continue to be attracted to it. And developers go where the users are. What's the point developing applications for platforms where there are no users! So, you want to build for the iPhone, but you don't want to build for the iPhone. How do you fix this?

The solution is simple. Move to an open platform. I'm not saying that you should stop building for the iPhone, and move to Android. That'd be stupid. I'm saying that you build for the browser, and serve both iPhone and Android users in one go. As a long-time web developer and currently exploring the mobile-WebKit browser, I can tell you that the mobile-WebKit browser is the best thing to happen to web development in a long time. It has imbibed standards support like no other browser has, even on desktop computers. HTML5 provides access to APIs that are mostly sufficient to build rich mobile applications. You've got excellent control on layout etc, since the CSS support is one of the best in the browser landscape. In fact, the browser in the latest phones is so good, I find it difficult to understand why I'd ever build a native application.

Here's a listing of some APIs that are available in the iPhone's and Android's browsers, that you probably didn't know existed. Yes, you can use all of these APIs today!

Of course, there are cases where you need to build a native application. Like if you want access to the camera. Or if you want to use the user's contact list. Or access the accelerometer. I'd still go native for those kinds of applications. However, very soon, that's likely to change as well, with the upcoming Capture API and Contacts API that are being spec'd. And I'm sure WebKit will one of the first browsers to adopt it.

On the whole, there's very little reason now to build native applications for the iPhone. Do it only if you know what you are doing, and you absolutely need to. Ask yourself before you start if you absolutely must go native. You'll discover that in most cases you won't need to. The browser is good enough. And you'll have supported other touch-phones while you are at it, for free, at its very least. At best, you've supported the entire mobile browser landscape, though that's a hard one, and realistically speaking you will compromise on the user experience.

One last concern remains. What if Apple decides to somehow ban browser based applications? Technologically speaking, I don't see how they can do that without reducing the power of the browser itself. One of the reasons the iPhone took off was because it shipped with a real browser. If they reduce the power of the browser, they reduce the usefulness of the phone itself. I don't see that happening. Secondly, the browser engine is actually developed independently of Apple — in fact Google contributes more to WebKit than Apple does. Unless Apple decides to revert to an older version of WebKit, or cherry-pick features from the public builds, they'll have to continue shipping the public builds of WebKit or feature-freeze on the current build. If anyone thinks Apple will (or can) control browser based applications, they're just plain wrong. The web is far too big for Apple to make such a move.

These are my thoughts. I'd be extremely interested in knowing what you think. Are you affected by this change? Are you going to move to the browser?

ShareThis