Getting Started With (and Gotchas of) CSS Media Queries
By Brian Suda
27 August 2010 | Category: Code, CSS3, Design
For all the nay sayers that think the iPad is just a big iPhone, you couldn't be further from the truth. Now, I won't go as far as to say it is "Magical", but with several million sold in the first month, it isn't a fad.
With it's wider screen and larger keyboard, there are several aspects you need to rethink as you are designing you web applications for this device. In this article I am going to walk you through how to adapt your web app to look and feel more like a native application that makes use of CSS media queries to change the layout when the device is rotated between horizontal and portrait modes.
The beauty of this progressive enhancement methodology is that you don’t need multiple versions of your code for desktop or mobile devices.

If we look at a common design pattern for applications, we tend to see some sort of left-hand navigation with a large main area to display the content. This is the way the iPad’s mail.app works.
The left-hand navigation is a list of email messages, which you can then select to see the full message in the main window. It also isn’t chance that this left-hand navigation is the same width as the iPhone screen to re-use many of the same widgets and design elements.
When the iPad is rotated into portrait mode, the screen width becomes too skinny to display a two column layout.

Therefore, the left-hand menu is hidden away as a button which toggles the menu as an overlay. I am going to walk you through how to do all of this using CSS media queries. It’s much easier than you would expect.
Setting up the HTML
Firstly, we need to set-up a template for our content will flow into. We’ll start with the standard two column layout and progressively enhance it to handle different screen widths.
1 2 3 4 5 6 7 8 | <div id="nav" class="saveSpace"> <ul> <li>Navigation Here</li> </ul> </div> <div id="main"> <h1>Content Here</h1> </div> |
Setting up the Basic CSS
With a bit of CSS we position these two items and give them specific widths and padding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #nav { width: 200px; height: 100%; border-right: 1px solid #ccc; position: fixed; top: 0; left: 0; z-index: 1000; } #main { margin-left: 200px; padding: 0; height: 100%; } .saveSpace { display: block; } |
This mimics a two column layout, but we only want it to display like this when the iPad is in portrait mode. Using CSS media queries, this is very simple.
There are a few key words, such as “max-width”, “min-width” to create rules based on width, but there are also rules for orientation and dpi. For more information, Ethan Marcotte wrote an excellent article on A List Apart called Responsive Web Design.
For our example, we are going to put the threshold at 800 pixels. Anything larger than 800px gets the two column view and anything smaller will see only the content area with a hidden menu, just like mail.app.
Adding in Media Queries
Our previous CSS has the basic layout for the two column mode, now we need to add in some additional CSS which over-rides some of the previous styles during certain situations of size. At the bottom of your CSS file, you can added the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @media all and (max-width: 800px) { #nav { border: 5px solid #131e31; position: fixed; top: 75px; left: 10px; height: auto; } #main { margin-left: 0px; } .saveSpace { display: none; } } |
It is important that this is at the bottom so that it takes precedence over the other rules declared above. This saves us from having to wrap everything above in a CSS query to explicitly say only apply this when min-width is 800.
With the#main, we have removed the left padding and therefore it no longer appears as a two column design. The #nav has been moved slightly away from the edge to make it appear more like a floating menu and finally, we are adding a style for .saveSpace of display: none. This will initially hide the menu until the JavaScript adds or removed that class to view the contents.
If you put this into your HTML and resize your browser above and below 800 pixels you will see that the design shifts from a one column to a two column design and back. This is a great tool for websites, not just web apps attempting to emulate native design.
Several websites have use this technique to make 3-4 column layouts at wide screen width and adjusting the number of columns depending if the screen is skinnier. Hicksdesign is an excellent example of reformatting the layout based on design.
Adding in the jQuery
In this example, the next thing we need to layer on is the ability to show and hide the left-hand menu. We can do this with a button that toggles the menu through a tiny line of jQuery. To do this we need to change our HTML inside #main to account for a menu button.
1 2 3 4 | <div id="banner"> <a id="menuButton" class="button" href="#">Menu</a> <h1>Menu Title</h1> </div> |
This gives is the app banner and a menu button, but we need to add the corresponding CSS to hide some of this when it is not needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #banner { border-bottom:1px solid #797f90; border-top:1px solid #fff; height:42px; text-align: center; } #banner h1 { margin: 0 auto; padding:0; color:#000; font-size:20px; line-height: 20px; padding-top: 8px; font-weight:bold; height:32px; white-space:nowrap; text-overflow: ellipsis; overflow: hidden; width: 55%; } #menuButton { position: absolute; top: 8px; left: 62px; display: none; right: auto; } |
Finally, inside our CSS media query we need to change the #menuButton so it is visible.
1 2 3 | #menuButton { display: inline; } |
After including jQuery,
1 | <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js" type="text/javascript"></script> |
We can add the following to the bottom of the HTML file.
1 2 3 4 5 6 7 8 9 | <script type="text/javascript"> $(document).ready(function() { $("#menuButton").toggle(function(){ $("#nav").removeClass("saveSpace"); },function(){ $("#nav").addClass("saveSpace"); }); }); </script> |
View the Demo
You can view a very basic working demo here. If you resize your screen you can see the left hand navigation change depending on the width. Notice that the content also re-adjusts to the presence of the left-hand navigation.
When the page is loaded, jQuery adds some behaviour to the #menuButton. The toggle function runs the first function the first time it is clicked and the second function the second time it is clicked.
On the third click, it runs the first function again and so on, toggling between the two. This is an easy way to create a show/hide button for our navigation. Rather than trying to show and hide the menu by directly adding display: none or block we add or remove a class called saveSpace.
This isn’t immediately obvious why, but because we are using CSS media queries to change our layout and the CSS is not re-applied.
If we had a hidden the #nav while in the skinnier mode and re-adjusted the width to make the screen wider than 800 pixels, the #nav would be hidden, even if we explicitly said in the CSS #nav {display: block;}. By adding and removing the class we get around this issue.
Issues with Media Queries
Has anyone noticed the issue yet? In this example we are using two different technologies to layer behaviour into our HTML. CSS Media Queries are run based on something the user does. They resize the browser and the design changes.
Previously, this was done with lots of JavaScript running checking window dimension and then applying different styles accordingly. Luckily, CSS media queries relieve us and the browser from all this hard work. But in this example, we are using JavaScript to show and hide the menu at skinny screen resolutions.
We need a way to progressively enhance this situation for browsers with JavaScript disabled. The problem is that instead of having a 1×2 matrix, JavaScript versus No-JavaScript, we now have a 2×2 matrix, JavaScript and CSS media queries, JavaScript and No-CSS media queries, No-JavaScript and CSS media queries and No-JavaScript and No-CSS media queries.
In this example, if the JavaScript is disabled, then we need to make the #menuButton a real link that takes you to the same page, but the server would need to update the CSS when it spat out the page so the menu was toggled from the previous state.
This would solve the no-JavaScript with CSS media queries issue. If your browser does not support CSS media queries, then this is less of a problem because the left-hand menu would never be hidden.
JavaScript to the Rescue?
There have been efforts using JavaScript to bring media queries to older browsers. By including an additional JS file (http://code.google.com/p/css3-mediaqueries-js/) it is possible to make sure your design behaves in a similar fashion across all your customers’ browsers.
The downside is that this requires JavaScript, but it does eliminate one of the 4 possibilities reported above, JavaScript/No-Media-Queries.
Mixing behaviour between two different technologies can cause more issues that need to be handled as browsers catch-up to supporting CSS Media Queries. This means more work on your part as a developer, but it produces a better experience for the customer.
Follow @thinkvitamin on Twitter Please check out Treehouse
