Learning about Progressive Enhancement - Supporting Mobile Browsers with CSS3 Media Queries
I blogged about how happy I've been working with designer Jeremy Kratz on my recent site redesign. We've been meeting lately about small improvements and tweaks. One of the core goals was to support many screen sizes from 30" down to a small smart phone screen. You can see the results on the right in a Windows Phone and iPhone.
My team is doing a lot of work on Mobile lately, not just with things like jQuery Mobile and what that means not just for ASP.NET but the mobile web in general. We've also worked with 51Degrees and they've even created a NuGet package that can help you tailor your site to any device. You'll hear more about Mobile in ASP.NET from Steve soon and I'll be sharing some ideas in a few upcoming conferences.
I originally enabled this blog for mobile browsing this same week in 2006. That's 5 years ago, when mobile was really just getting started. Back then, I had a separate mobile view that was stripped down and emitted totally different markup based on user-agent sniffing. This was pretty innovative for the time, a half decade ago. There's a number of things in ASP.NET 2 (what this blog runs on) that do adaptive markup. However, more modern techniques use a combination of detecting capabilities on the server side with feature detection on the client. We query the browser with JavaScript: Do you support Geo-Location natively? No? How about using this shim JavaScript library?
When laying out a page, rather than serving up different markup on the server based on the server-detected browser, we can also use CSS3 media queries that modify a layout based on screen (viewport) size. I wanted to use these techniques for this blog.
While I realize that some people want a totally custom iPhone web application when they visit a site, I draw a specific line between a Web Site, a Mobile Web Site and a Mobile Web Browser Application. When visiting what I consider a regular web site, I don't want an iOS-specific Web version and a Regular Web I can't see. What I'd like to see is the site I went to, perhaps with alternative elements or a few things moved around.
However, if the site is full of interactivity, I might want a Mobile Web Site served up with different - perhaps device-specific - markup for different Smart Phones. I think the decision point for how application-like, and therefore device-specific, your mobile website should be hinges on how much your user will be interacting with it vs. just reading/consuming content.
With this blog, there's not a lot that needs to be smart phone specific. I want a mobile-optimized version of the site, not a complete iOS (or Android, or WP7 or whatever) re-imagination of the site. If I did, I'd download an app.
If you're creating a Web Application that focuses on "producing" (meaning pushing buttons, doing application stuff) I can see using a phone-specific framework, but when "consuming" content, I'd like my Web Site to look like a Web Site.
CSS3 Media Queries
Designer Jeremy and I (mostly him!) came up with a goal to support desktops, tablets and netbooks around 1024 pixels wide, and phones less than or around 720 pixels wide.
Here's the general structure of the CSS. Note that none of this is JavaScript. This is all just descriptive "when the screen is like this, apply these styles" stuff.
/* psuedo CSS, not complete */
/* CSS Reset from http://meyerweb.com/eric/tools/css/reset/
Goes here
*/
/* Global Styles for the Desktop Site */
/* Force the images to resize smaller
when they get squished with small screens */
img
{
width: auto !important;
height: auto !important;
max-width: 100%;
}
/* bunch of styles */
/* Hide the dropdown we'll use for
navigation when we are tiny */
#nav select
{
display: none;
}
/* Tablet device styles */
@media screen and (max-width:1024px)
{
/* hide stuff, make things smaller, yank the right rail */
#sidebar
{
display: none;
}
/* hide other stuff */
}
/* Phone device styles */
@media screen and (max-width:720px)
{
/* hide the huge top nav and show the nav dropdown */
}
Desktop vs. Mobile Site Navigation
Everything looks nice, until you resize the site and the top navigation bar really got squished, then overflowed. However, Jeremy had a great idea. The navigation at the top is nice, but when the screen is REALLY small the navigation is overwhelming. The navigation is just an unordered list like this:
<div id="nav">
<ul id="primarynav">
<li id="nav-info"><strong>Info</strong>
<ul>
<li><a title="Learn more about Scott Hanselman" href="http://www.hanselman.com/blog/AboutMe.aspx">About Me</a></li>
<li><a title="Contact Scott Hanselman" href="http://www.hanselman.com/blog/contact.html">Contact Me</a></li>
</ul>
</li>
<ul>
...more..
</div>
Why not show that as a dropdown for tiny screens? There's actually a StackOverflow question on that (did you know Jeremy did the initial StackOverflow design?) I could certainly just put a dropdown in a DIV and hard code it, but since the structure already exists in a UL and I might want to change it in the future as well as avoid duplication, we'll use JavaScript to inject the SELECT into the HTML DOM (Document Object Model).
Basically we just spin through the UL structure and create the select, options and (bonus!) option group using the existing hierarchy.
$(document).ready(function() {
var markUp = ["<select id='primarynav-select'>"], $li, $a;
markUp.push("<option value='' selected='selected'>Go to...</option>");
$("#primarynav > li").each(function(){
$li = $(this);
if($li.find("li").length){
markUp.push("<optgroup label='"+$li.find("strong").text()+"'>");
$li.find("li").each(function(){
$a = $(this).find("a");
markUp.push("<option value='"+$a.attr("href")+"'>"+$a.text()+"</option>")
});
markUp.push("</optgroup>");
}
else{
$a = $li.find("a");
markUp.push("<option value='"+$a.attr("href")+"'>"+$a.text()+"</option>")
}
});
markUp.push("</select>");
$("#primarynav").after(markUp.join(''));
$("#primarynav-select").change(function(){ window.location = $(this).val(); });
});
So this,
becomes this, when you resize the browser really small, just by hiding the one and showing the other using the CSS above.
Resizing Images for Small Screens
For an important site that gets a lot of mobile traffic, you would want to use a browser database like 51Degrees that could tell you the size of the screen of a specific phone and the graphics formats (PNG, JPG, transparency or not) so you could resize large images on the server side and return mobile-optimized images. You could have a handler that detects small phones with small screens then resizes and caches images. Perhaps even grayscales them for non-color phones.
In my case, my images aren't that large and it's not that important to me to do server-side resizing. Instead we decided to have the images resized on the client side using CSS like this:
img
{
width: auto !important;
height: auto !important;
max-width: 100%;
}
This says, no matter what other CSS or the markup says, it's important that images resize to 100% but no larger. This was a nice simple solution for those larger photos that I put at the top of each post. Rather than overflowing and clipping against the edge of the phone, then resize. You can see this in action by just resizing your browser.
Here you can see this site on an IPad. Note the right rail has disappeared. On the right, the same post on an iPhone 4 which is 640px wide. See how the image has moved up and is the width of the phone?
You can even use CSS Media Queries, without any JavaScript, to detect iPad orientation, or if the user has an iPhone 4 with high-res retina display vs. a low-res iPhone 3G.
Here's example CSS from Web Designer Wall, credited to Thomas Maier and Cloud Four respectively.
<!-- Detect iPhone4 Retina Display from CSS -->
<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" type="text/css" href="iphone4.css" />
<!-- Detect iPad and iPad orientation -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">
You can actually test and simulator the orientations locally by making your desktop browser wider than it is tall.
There's still a few things left to tweak, and these techniques only work with modern Smart Phone browsers that support these features, but for the 8% of you who are reading this blog on a mobile device it should be a better experience now.
One Caveat - Code Samples
As with all nice things, there is one bad thing. The code samples that I have use SyntaxHighlighter. It takes a <pre> and using JavaScript does a lot of work on them, effectively replacing the PRE in the DOM with a bunch of stuff. This looks lovely in a desktop browser and crappy on mobile. I'm still exploring how to fix this. Here's some ideas I have.
- Conditionally override the SyntaxHlighter CSS using Media Queries
- Change the plugin to keep the <pre> around, but hidden. Then swap the highlighted one with the plain one on phones
- Come up with better CSS all up for the highlighter
I'm interested in your thoughts!
UPDATE: I'm currently reading Adaptive Web Design: Crafting Rich Experiences with Progressive Enhancement as it's amazing. Aaron gave me a review copy to check out. He outlines a similar UL to SELECT enhancement and a dozen others in a clear, clean readable manner. I'm enjoy it thoroughly.
Related LInks
- How To: Add Mobile Pages to Your ASP.NET Web Forms / MVC Application
- 51Degrees on CodePlex
- CSS Media Queries on Web Designer Wall
- Tailoring your Blog to your Readers' Browsers
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
as for the code samples - with my iphone4 they look great. you can scroll with two fingers. A distinct background or border would make it clearer that it is a separate, scrollable area
Mango is close enough that going this route is the best (only) choice overall, but ding dong dang it how annoying that sites look so much better on Android and iPhone (and WebOS!).
If Mango did nothing but smite IE 7++ with a +5 Mace Of Mango-y Justice and obliterate it from WP7, then Mango would be a hugely worth-while upgrade...
In fact if I were Emperor Of The Known Universe, I would require Microsoft to include a WebKit based browser on WP7 FOR THEIR OWN GOOD. Even if they keep IE. I would be fine with two browsers on my device if one of them worked well...
My HTC Trophy is my favorite phone so far, except for the browser (and browser control for Silverlight). That and multi-tasking. (No, this isn't going to descend into the Monty Python skit about the Inquisition.)
I certainly plan to use these techniques (awesome work, by the way) in my own development and impatiently await the release version of Mango.
May be you should check 320 and up of Andy Clarke http://stuffandnonsense.co.uk/projects/320andup/ it is nice boilerplate for creating different versions of your site.
Maybe the next thing you should care to make the markup a bit more manageable.
But overall, very cool post. I'm needing to implement these types of tricks in our new site at work too.
As I said earlier, it wouldn't make sense to compromise going forward to look good on a browser with a shelf life of <please insert official Mango OTA update date - today's date here>.
Just wishing the browser upgrade could have come sooner, or we had additional browser choices on the device.
The page looks nice, but it is painful to wait until it loads on an iPhone, even on a wifi.
Do you really need to show all those avatars in the comments. 150 http requests is little extreme, even on a desktop.
If you can reduce the requests and minimize the onload JavaScript, the page will download and render much faster.
If you create the navigation select on the server, instead of with JavaScript will help.
The Microsoft approach of including tons of static content, just in case, is not very effective on mobile devices :)
Also, the gravatars are async and cached.
I'll run YSlow and see what I can do to speed things up.
Scott
If you're using CSS to hide elements (like the big top nav) for mobile devices, that means you're still shipping the markup for that section across the internet to the user's device. For things like the top nav isn't it possible to just omit that section completely if you know that the user-agent or the user-agent capabilities are those of a mobile device?
Adam - Potentially, but we're talking about less than 1k of markup.
Great work!
Now I'm ready to dig into these mobile formatting tips.
This is my first comment on your blog :p
I come here everyday and i wanted to say that i never found an interesting blog like yours.
The power of your blog is the way you share different subjects (tech, life, ...). It's really attractive.
So ... Today i just want to share my feedback about your new blog layout on mobile.
Few days ago, it was ok (nothing to say) but now i've a scrollbar at the bottom for each post and it's annoying.
Did i say that i'm using the Samsung Galaxy S2 defaut browser ?
I don't know how i can help to give you more details but i'm sure there is a solution :p
Keep up the good work !
Regards,
How are you testing this site for different browsers & devices?
Thanks,
Navin
first of all great work on this site and tanks for sharing your knowledge with us.
This might be a kind of stupid question, but... which WP7 phone is that in the picture?
It is going pretty so far ;)
Comments are closed.