Curated by Carsonified

Learn with Treehouse

Accessibility, CSS3, Design, Django, HTML & CSS, HTML5, JavaScript, jQuery, NoSQL, PHP, Responsive Web Design, Ruby, Ruby on Rails, Tools, UX, Version Control, WordPress, iOS and more

Article 16

Getting Started With (and Gotchas of) CSS Media Queries

By

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.

View Final Demo

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

Other Posts You Might Find Interesting

  • Sorry - No Related Posts Found

Comments

  • http://www.twitter.com/sturobson Stuart Robson

    I’m loving the idea of media queries, unfortunately slow connection speeds make hard work for all those lovely big images that need downloading when you’re on 56k (it still happens) or 3G or edge.

    A solution for browsers and mobile browsers I’m working on with the help of @craftedpixels using jquery (so only browsers that support jquery) by adding a hook to all the images to download a smaller version of the image. This is still very much a developing idea and not a solution. Here’s the code

    $(document).ready(function() {

    if ((screen.width<=960) && (screen.height<=640)) {

    $("#someid").addClass("iphone");

    }

    });

    I'm still researching who, what, where, when & why as to how this actually works and will hopefully have it as a first post in my blog :)

    Stu

  • http://adactio.com/ Jeremy Keith

    A simple way to make sure that certain CSS rules are only applied when JavaScript is available is to add a class to the body element using CSS:

    $(‘body’).addClass(‘hasJS’);

    Then, when you hide something using CSS, make sure you include that class so you know that the element will only be hidden when JavaScript is enabled:

    .hasJS .saveSpace { display: none }

    This way, in browsers that don’t support JavaScript, the content will always be available.

  • http://www.deepubalan.com Deepu Balan

    Really an interesting post… Very well written with nice simple examples… Yeah CSS media queries can save lot of our valuable time… Interesting to know that there is a javascript solution to run media queries in older version browsers…
    Thanks… Bookmarked! :-)

  • http://adactio.com/ Jeremy Keith

    Sorry, that first sentence of mine should read “A simple way to make sure that certain CSS rules are only applied when JavaScript is available is to add a class to the body element using *JavaScript*”.

  • http://twitter.com/sturobson stuart robson

    I’ve been loving all this chatter about media queries, my only issue was that of connection speed. If you’re designing for the big browser with big images when it gets down the line of a EDGE (2G) signal it’s going to take a while. I thought this was something that could be fixed with jquery (but not on mobile browsers that don’t support it).

    I came up with a quick jquery solution with help from @createdpixels

    $(document).ready(function() {

    if ((screen.width<=960) && (screen.height<=640)) {

    $("#someid").addClass("iphone");

    }

    });

    I haven't fully tested this bit of coding but I will hopefully publish a bit of a blog when I have done.

    When trying this out on a live site along with media queries I noticed that the good old blackberry browser dumps at the though of anything (well definitely background images) hooked in to a @media query within the css. I've found if you code for the less capable mobile browsers first then ramp up within media queries this sorts it out.

    Regards

    Stu

  • http://suda.co.uk Brian Suda

    Excellent, Thanks Jeremy.

    The four possible scenarios are:

    1. No-JS/No-CSS Media Queries
    Lowest common state. We know the base-line works because we have progressively enhanced the page from this point.

    2. JS/CSS Media Queries
    This is the ideal solution where everything would work as expected.

    3. No-JS/CSS Media Queries
    Using Jeremy’s trick of injecting a class attribute on the BODY, if there is no JS then that class will be absent and all your CSS that has that selector will fail and no Media Query Rules will try to be applied

    4. JS/No-CSS media Queries
    This is solved by including the JS file which gets older browser to act as if they have media queries

  • http://askthecssguy.com Tony White

    Thanks for the article… I’m working through these very bits, so it’s nice to peek over the fence and see how others achieve this. I had been playing with (orientation:portrait) vs (orientation:landscape), but I see the value in sniffing out width only.

    Also liked seeing Jeremy Kieth’s first comment. I’m surprised how much I make use of the javascript-conditional class names

  • http://www.legendofdrew.com/ Drew

    Or, you could get a primitive “click to reveal” with only CSS by using :focus and :active. If you put #nav as a descendant of #menuButton, you, in theory, do something like this.

    #nav{display:none}
    a#menuButton:focus #nav, a#menuButton:active #nav{display:block}
  • http://annuairepagesblanches.org/ Kevin Cornella

    Super article!
    Thanks

  • http://www.websolpk.com imran khan

    Great Article!!! i actually like the concept of media queries…

    Its really good to read…

  • http://gauravmishra.com Gaurav Mishra

    ☑ Bookmarked for the future

  • http://howtobeatherapist.co.uk/ paulmorales

    I never thought this css was possible until now

  • ZeeVoo

    Oh wow this makes very good sense to me dude.

    http://www.anon-surf.at.tc

  • http://code.google.com/p/css3-mediaqueries-js/ Wouter van der Graaf

    Nice to see my css3-mediaqueries.js mentioned in your article. Thanks!

  • http://www.confessionsofamanunitedfan.com george best

    those are really neat tricks… thank you for putting this together!!

  • Cal

    I know that this doesn’t deal with the iPhone size, but I am having some trouble with a media query.

    On the site: http://www.montgomerycenter.org/newindex.html in the header I have put the media query for iPhone size, iPad size and one for regular browsers. Although when I load the site into my iPhone it doesn’t use the phone.css, it uses the regular css.

    Can anyone figure out what I am doing wrong?

Badges for Treehouse

Treehouse

Learn iOS, Rails, CSS3, jQuery, Node.js, HTML5, UX and more in less than 8 minutes per day. New videos added regularly. Sign up today and get a free Web Design Toolkit.

Ads Via The Deck

Think Vitamin Radio
Episode #34: Amazon Fire and Responsive Roundtable

Check out our bi-weekly radio show. Covering the hot topics on the web.

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Download Podcast as mp3

Advisory Board

The Think Vitamin Advisory Board in place make sure that you receive the best content possible.