Advertisement

Examining Responsive Navigation: Off Canvas Patterns

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →
This post is part of a series called Examining Responsive Navigation Patterns.
Examining Responsive Navigation: Toggle Patterns

In this tutorial we'll walk through four variations of patterns where navigation and page content is placed off canvas to conserve space until requested. It's time to stop copying and pasting, let's start understanding!

To recap, the first in this series focused on patterns where the navigation remained in the header. The second looked to patterns that moved the navigation to the footer. The third discussed patterns where the menu could be turned on and off.

With all of these patterns the goal has been to minimize the vertical space navigation occupies on smaller screens. Today, we'll move parts of the page off canvas until they're requested.

As with the previous articles these patterns are probably familiar to you through Brad Frost who compiled some of the patterns being used on responsive sites. Luke Wroblewski is another who's been popularizing the idea of off canvas patterns.

The Patterns

With the header and footer patterns, we mostly rearranged the navigation for various screen sizes. With the toggle patterns we hid the navigation when space became tight and used a button to unhide the navigation. Off canvas patterns work similarly to the toggle patterns in that they're out of sight when we need to conserve space and can be requested with a button. Instead of being completely hidden though, they're just off the screen or even partially visible.

There are a variety of combinations of where and how different parts of the page can move offscreen. I want to focus this tutorial on four pattern variations.

  • Sidebar Navigation — The menu is located in a sidebar off canvas left. Clicking a button slides the menu in and slides most of the content off canvas right. We'll use the checkbox hack for the click event.
  • Sidebar Navigation JS — This is the same pattern as above, except we'll use Javascript instead of the checkbox hack for the click event.
  • Panels — This pattern places different content in panels. Navigation at the top of the page slides the main content to the panel requested. We'll use radio buttons for the click event.
  • Sidebar+ — This pattern works similar to the one above, except it allows more independence in opening and closing content. We'll also use Javascript for the click events.

As we've been doing throughout this series, the concepts in each pattern build on those that come before it. In this tutorial we'll consider a couple of different ways to structure your html to move sections on and off canvas. The css for each will be similar, though it will naturally vary with the different structures. We'll also use both css (checkbox and radio buttons) and Javascript driven click events. Ideally by the end of this tutorial you'll have a good understanding of the basic techniques involved and will be able to modify them to suit the needs of your project.

The complete code for all of these patterns is available through the download link above and can also be seen by visiting the demo and viewing the source code.

The menu button visible and the menu hidden on small screens

The Sidebar Nav Pattern

This is the simplest and probably most common off canvas pattern. By default the page will open as a single column of content. The only visible navigation will be the lone menu button. Clicking the button will cause the menu to slide in from the left filling most of the screen. The single column of content will mostly slide off the screen to the right, though some of it will remain visible.

Approach: On small screens we'll hide the menu and replace it with the menu button. The button is tied to a checkbox with gets checked and unchecked creating 2 states. When clicked everything slides to the right revealing the full menu. Clicking again on the menu button or the close button we'll add to the top of the menu slides everything back to the left. As the browser is resized larger we'll convert the menu to a horizontal navigation bar as well as convert our single column layout to a 2 column layout.

Step 1: The HTML

With these patterns it's important to see the structure for all the major pieces so I've included more than the menu, though I've stripped out most of what's inside these major pieces to keep things more readable.

<input id="toggle" type="checkbox" />
<nav>
  <label class="close" for="toggle" onclick><span>X</span> Close</label>
  <ul id="nav">
    <li><a href="">Back to Post</a></li>
    <li class="current"><a href="sidebar-nav.html">Sidebar Nav</a></li>
    <li><a href="sidebar-nav-js.html">Sidebar Nav JS</a></li>
    <li><a href="sidebar+.html">Sidebar+</a></li>
    <li><a href="panels.html">Panels</a></li>
  </ul>
</nav>

<div class="wrapper">
  <div class="inner">
    <header>
      <label class="btn" for="toggle" onclick>Menu</label>
    </header>
		
    <div class="container main-content">
      <div id="content"></div>
      <div id="sidebar"></div>
    </div>
      
    <section class="subfooter"></section>
    <div id="footer"></div>
  </div>
</div>

The important part to notice is that most of the content is wrapped with 2 additional divs, a wrapper div and an inner div. Note that the "menu" button is inside the header that's inside these additional divs. That means the button will slide to the right with our content when clicked.

The navigation, however, is outside these 2 additional divs and includes the checkbox we'll check and uncheck. Inside the nav element we'll create a label that's connected to the checkbox for the purpose of closing the menu, though the menu button can still be used to close the menu as well.

This structure creates 2 independent parts. One for the menu and one for everything else.

Step 2: The Default CSS

If you read the previous tutorial on toggle patterns, you'll recognize the button and it's css. The style of the button uses the same colors, border-radius, and gradient as the previous tutorial.

What's different is we've now moved the button to the left since the menu is off canvas left. Also instead of using positioning, here the button is floated left. This means when everything slides to the right, the button will slide to the right as well.

.btn {
  float: left;
  margin: 1.5em 0 0 0;
  background: #999;
  padding: 0.25em 2%;
  color: #fff;
  cursor: pointer;
  border-radius: 0.25em;
  background-color: #5b5756;
  background-image: -webkit-linear-gradient(top, #6b6766, #5b5756);
  background-image:    -moz-linear-gradient(top, #6b6766, #5b5756);
  background-image:     -ms-linear-gradient(top, #6b6766, #5b5756);
  background-image:      -o-linear-gradient(top, #6b6766, #5b5756);
  background-image:         linear-gradient(top, #6b6766, #5b5756);
}
	
.btn:hover {
  background-color: #7b7776;
  background-image: -webkit-linear-gradient(top, #8b8786, #7b7776);
  background-image:    -moz-linear-gradient(top, #8b8786, #7b7776);
  background-image:     -ms-linear-gradient(top, #8b8786, #7b7776);
  background-image:      -o-linear-gradient(top, #8b8786, #7b7776);
  background-image:         linear-gradient(top, #8b8786, #7b7776);

We need the checkbox for its functionality, but we don't need to ever see it so we'll position it far off the screen.

#toggle {
  position: absolute;
  left: -999em;
}

For now we want the navigation off screen. We'll fix it's position since we don't want it to scroll when visible and we'll also set the height to 100% so it fills the height of the screen. If you have more links, you might not choose to do either, but since we only have a few links this should be ok.

When the menu is later visible we don't want it to fill the screen horizontally. Setting the width to 75% will allow us to show part of the content when the menu is visible. To move the menu off screen we'll set it's left value to -75% to match the width. We'll also set a transition so the menu slides in smoothly instead of appearing all at once.

The remaining styles add a background and some padding to push the links below the close label.

nav {
  position: fixed;
  left: -75%;
  width: 75%;
  height: 100%;
  padding: 5em 0 0 0;
  background: #3b3736;

  -webkit-transition: left 0.5s;
     -moz-transition: left 0.5s;
      -ms-transition: left 0.5s;
       -o-transition: left 0.5s;
          transition: left 0.5s;
}

We want the close label to function like a button so we set a cursor value. What's perhaps most interesting here is how we style the X that was located in a span inside the close label. To create a circle around the X we add a border and then a border-radius of 50%. To make it more circular I adjusted the top/bottom and left/right padding by eye. Note that a different typeface would require these padding values to change.

.close {
  cursor: pointer;
  color: #fff;
}

.close:hover {
  color: #999;
}

.close span {
  border: 2px solid #fff;
  border-radius: 50%;
  padding: 0.2em 0.4em;
}

The css for the list of links is for aesthetics only. The top margin of the list is given a small bump, but otherwise margins and paddings have been zeroed out. The links get a bottom border and so to complete the effect, the list itself gets a top border.

The links are also given a gradient background to give them a slight bit of depth and their height has been exaggerated to 4em to create a large tap area.

#nav {
  margin: 0.1875em 0 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid #777;
}
		
#nav a {
  text-decoration: none;
  color: #fff;
  padding: 1em 0 1em 5%;
  display: block;
  border-bottom: 1px solid #777;
  height: 4em;
  background-image: -webkit-linear-gradient(top, #4b4746, #3b3736);
  background-image:    -moz-linear-gradient(top, #4b4746, #3b3736);
  background-image:     -ms-linear-gradient(top, #4b4746, #3b3736);
  background-image:      -o-linear-gradient(top, #4b4746, #3b3736);
  background-image:         linear-gradient(top, #4b4746, #3b3736);
}

#nav a:hover {
  background: #4b4746;
  background-image: -webkit-linear-gradient(top, #5b5756, #4b4746);
  background-image:    -moz-linear-gradient(top, #5b5756, #4b4746);
  background-image:     -ms-linear-gradient(top, #5b5756, #4b4746);
  background-image:      -o-linear-gradient(top, #5b5756, #4b4746);
  background-image:         linear-gradient(top, #5b5756, #4b4746);
}

At first glance the css for the wrapper and inner divs might not seem to do a lot. Both are set to 100% width and the wrapper div gets an additional overflow: hidden. This will be important when we slide the content to the right in the next section. The inner div is floated to the right and we give it a transition to match the nav element above.

.wrapper {
  width: 100%;
  overflow: hidden;
}

.inner {
  float: right;
  width: 100%;

  -webkit-transition: 0.5s;
     -moz-transition: 0.5s;
      -ms-transition: 0.5s;
       -o-transition: 0.5s;
          transition: 0.5s;
}
The menu with close button visible and the everything else pushed mostly off the screen

Step 3: The CSS to Reveal the Menu

We're using the checkbox hack for the click event so to target elements after the button has been clicked we can use :checked in combination with a sibling selector. It's actually quite easy to slide everything to the right.

For the nav element we just set it's left value to 0. With that one change the entire menu will slide into view and fill 75% of the screen (since we set its width to 75%). Now that the close link can show we fix it to the top and left (You can fix and located the close button in the default css as well. I chose to do that here, but there's no specific reason behind that choice).

:checked ~ nav {
  left: 0;
}
	
:checked ~ nav .close {
  position: fixed;
  top: 1.5em;
  left: 4%;
}

To slide the content to the right we give it a right margin of -75%. This is where it was important for the wrapper div to be set to overflow hidden. Had that not been set the overall width of the page would increase. The content would be off to the right, but we'd be able to scroll left and right to reveal and hide it. We don't want that. With overflow: hidden set on its parent container anything that would have been offscreen is now hidden.

:checked ~ .wrapper .inner {
  margin-right: -75%;
}
The menu visible as a horizontal navigation bar on larger screens

Step 4: The CSS In Media Queries

Once the screen is wider than 48em we have enough room to fit the menu horizontally at the top of the screen. It's a tight fit, but we can still convert the menu to a horizontal navigation bar.

We'll hide the menu button since it's no longer needed and move the logo to the left. As everything in the header will be either floated or positioned we need to make sure the header itself doesn't collapse. Here a little bottom padding works to do that.

@media screen and (min-width: 48em) {
  header {
    padding-bottom: 5em;
  }
  
  .logo {
    float: left;
    margin: 1.25em 0;
  }
	
  .btn {
    display: none;
  }
}

Just in case the width of the browser was increased while the menu was open and the content off to the right, we'll reset the margin to 0. We'll also turn off the transition as we no longer want one if the browser is resized again.

@media screen and (min-width: 48em) {
  :checked ~ .wrapper .inner {
    margin-right: 0%;
    -webkit-transition: 0;
       -moz-transition: 0;
        -ms-transition: 0;
         -o-transition: 0;
            transition: 0;
  }
}

To move the navigation we'll now float it to the right. We also have to set the position back to the default static and since we no longer need the background we'll set it back to transparent. While we're at it we'll reset height, padding, width and we'll turn off the transition.

Notice that this is done to both the nav and the nav in the checked state. You never know when someone will start with their browser small and after clicking the menu button, resize the browser wider. We might as well account for that possibility.

@media screen and (min-width: 48em) {
  nav,
  :checked ~ nav {
    float: right;
    position: static;
    background: transparent;
    padding: 0;
    height: auto;
    width: 100%;
    -webkit-transition: 0;
       -moz-transition: 0;
        -ms-transition: 0;
         -o-transition: 0;
            transition: 0;
  }
}

We'll never need to see the close link once our navigation is always visible so we'll set its display to none. Again note we're doing this for both checked and unchecked states.

@media screen and (min-width: 48em) {
  nav .close,
  :checked ~ nav .close {
    display: none;
  }
}

Finally we'll convert the list from vertical to horizontal. We'll use absolute positioning on the list, float the list items and return them to display inline. A bit of padding on the links and resetting a few things like the border and background and we're all set.

@media screen and (min-width: 48em) {
  #nav {
    position: absolute;
    top: 1.5em;
    right: 2%;
    border: 0;
  }
			
  #nav li {display: inline; float: left;}
  #nav li.current a {color: #7b7776;}
			
  #nav a {
    padding: 0 1.5em;
    display: inline;
    border: 0;
    background: transparent;
  }
				
  #nav a:hover {background: transparent;}

  #sidebar {
    margin-top: 5.5em;
  }
}

Step 5: A Fix for iOS and Android

I covered this last time in the toggle buttons, but it doesn't hurt to repeat it in case you missed that post. The checkbox hack has some issues in iOS below version 6 and Android version 4.1.2. Fixes for both are simple though hardly obvious.

The iOS fix is an empty onlclick event on the label connected to the checkbox. If you scroll back up you can see this empty onclick in the html at the start of this pattern. The Android fix is a fake animation on the body element as seen below.

body {
  -webkit-animation: bugfix infinite 1s;
}

@-webkit-keyframes bugfix { 
  from {padding:0;} 
  to {padding:0;} 
}

And with that we're done with the off canvass sidebar menu.

Thoughts

This pattern works well for a few reasons. It conserves space by hiding the menu until it's requested. A simple transition and slide to the right makes it clear what's happening. On mobile devices the menu is always just a tap away.

The keys to the pattern are setting up a click (or tap) event and here the checkbox hack was used. Based on the state of the checkbox left, right, margin-left, or margin-right values can be adjusted so parts of the page are located where you want. Here the menu took up 75% of the page when visible, but it could easily have been 100% of 50% or any other value.

Another important key is to set overflow hidden on the wrapper div to ensure the inner div completely disappears and can't be scrolled to when off canvas. The button alone is what we want to move the page content around.

Examples

Below are some examples of this pattern that use css click events. The examples vary the pattern a little and 2 of them use the :target hack for the click event instead of the checkbox hack.

The Sidebar Nav Js Pattern

This pattern is pretty much the same as the one above. There's really only major one difference and that's here the click event will be controlled by Javascript, which is still more commonly used in off canvas patterns. We'll modify the pattern above so it uses jQuery instead of the checkbox hack.

Approach: On small screens we'll hide the menu and replace it with a menu button. Our jQuery function will intercept the click and add or remove a class so our css can become aware of state. Styles set on these classes will display the page layout with the menu open or closed. Once again as the browser is resized larger we'll convert the menu to a horizontal navigation bar as well as convert our single column layout to a 2 column layout.

Step 1: The HTML

The html in the jQuery version of the pattern is nearly exact to what we saw above. The checkbox input has been removed and the two labels have been converted to links. Otherwise everything below is the same as we saw in the checkbox version of this pattern.

<nav>
  <a class="close"><span>X</span> Close</a>
  <ul id="nav">
    <li><a href="">Back to Post</a></li>
    <li><a href="sidebar-nav.html">Sidebar Nav</a></li>
    <li class="current"><a href="sidebar-nav-js.html">Sidebar Nav JS</a></li>
    <li><a href="sidebar+.html">Sidebar+</a></li>
    <li><a href="panels.html">Panels</a></li>
  </ul>
</nav>

<div class="wrapper">
  <div class="inner">
    <header>
      <a class="btn">Menu</a>
    </header>
		
    <div class="container main-content">
      <div id="content"></div>
      <div id="sidebar"></div>
    </div>
      
    <section class="subfooter"></section>
    <div id="footer"></div>
  </div>
</div>

Step 2: The Default CSS

There's even less difference here. The default css is exactly the same in both versions of the pattern.

The menu visible with everything else mostly off screen right

Step 3: The CSS for the Open Menu

Here things are also similar, but we do have to make a few changes. The css properties and values are the same as above, but they need to be added to different selectors. We'll see how these classes are added and removed momentarily, but you can already see a few things by looking at the selectors.

We'll be adding or removing an .open class to the nav element. In the version above we had to grab the nav element as a sibling of the :checked pseudo selector. We'll still make use of sibling selectors to target the .inner div as the nav element and the .wrapper class are siblings.

nav.open {
  left: 0;
}

nav.open .close {
  position: fixed;
  top: 1.5em;
  left: 4%;
  display: block;
}

.open ~ .wrapper .inner {
  margin-right: -75%;
}

The properties and their values are the same as we saw above.

Step 4: The CSS In Media Queries

Once again the css is the same as it was in the checkbox version of this pattern. The only difference is that :checked ~ nav is replaced with with nav.open and :checked ~ .wrapper .inner is replaced by .open ~ .wrapper .inner. just as in the section above.

I'll trust you don't need to see the code again since it really is nearly identical. I will remind you though, that it's still a good idea to target both the open and closed versions of the nav element just in case someone first opens the menu and then resizes their browser so we'll style both nav and nav.open and include both for their descendent selectors we style as well.

Step 5: The JavaScript (jQuery)

Sorry to keep you waiting. This is really the meat for this version of the pattern. We start by intercepting the click on any link with a class of .btn added. Here that's our lone Menu button.

We want to add and remove the class on the nav element. If you remember our button (a.btn) is inside our header, which is inside the .inner div, which is inside the .wrapper div, which is a sibling to the nav element. To bubble up from the button to the .wrapper div we'll use the jQuery parents() function, which finds the nearest ancestor that matches instead of only the most immediate one. From there we can grab the sibling nav element.

We'll test to see if the nav element already has the class applied and if it does, we'll remove the class. Otherwise we'll add it. While most people probably won't reach for the Menu button to close the menu given the layout, we might as well let them in case they do.

$(document).ready(function() {
  $('a.btn').click(function() {
    if($(this).parents('.wrapper').siblings('nav').hasClass('open')){
      $(this).parents('.wrapper').siblings('nav').removeClass('open');
    } else {
      $(this).parents('.wrapper').siblings('nav').addClass('open');
    }
    return false;
  });

  $('a.close').click(function() {
    if($(this).parent('nav').hasClass('open')){
      $(this).parent('nav').removeClass('open');
    }
    return false;
  });
});

The close button is inside the nav element so it's a more direct path from it to nav. Here we only need the singular parent() function and need look no further. Also since the close button will only show when .open is present on nav we don't need to add it. In fact I probably didn't need to test to see if it's present, since it has to be.

Thoughts

If you understood the checkbox version of this pattern, this alternate version using jQuery should be pretty easy to understand as well. We removed the checkbox and converted labels to links. In the css anywhere we previously had :checked ~ nav we now use nav.open and anywhere we previously had :checked ~ .wrapper .inner we now use .open ~ .wrapper .inner

The new code is really the jQuery function for adding and removing the class (the equivalent of checking and unchecking the checkbox). If you know jQuery it should be simple to understand the function. Hopefully it's not too difficult even if you aren't familiar with jQuery. We intercepted the click and then used some jQuery functions to traverse the DOM in order to get from the click to the nav element we wanted to modify.

Which of the two versions you use is up to you. Neither is perfect. The checkbox hack needs a couple of fixes for iOS and Android and won't work in older versions of IE. Javascript is most likely installed and active for anyone who visits your site, though most likely isn't the same as always. Odds are with either option you're going to lose a few visitors, though doubtful many.

Examples

Below are additional demos that utilize this pattern and use Javascript for the click event. If you take a look at all of them, you'll see the demo here is modeled on the one by David Bushel and I've included Dave's tutorial. The Meny demo will look every different from any of the others, but at it's core it's still essentially the same pattern. An event is fired and through that event a sidebar menu is revealed and the main content changes.

The Panels Pattern

This pattern is different from the two above, though it still relies on the same basic ideas. Here we'll structure the 3 sections of the demo (menu, main content, and sidebar) as 3 separate panels of content. On the smallest screens only one panel will ever show at a time. We'll include a new additional menu at the top of the page that offers an option for each panel. Clicking any brings the panel it's connected to onto the screen with the other panels moved off screen.

Approach: On small screens we'll only show one panel at a time, with the main content being the default panel visible. We'll create 3 buttons in the header, one for each panel. These buttons are all styled radio buttons. We'll use something similar to the checkbox hack (the radio button hack) to control the click event. Where a checkbox allows for two states, radio buttons allow for as many states as we want. As the browser is resized we'll show more panels on the screen at a time. First we'll show two panels with one hidden and finally we'll show all three panels by default.

Step 1: The HTML

A few things have changed in the structure of the html from the previous two patterns. First the nav element with our menu is now inside the .wrapper and .inner divs. This will allows us to slide all three panels at once by making changes to their parent div, .inner.

Both header and footer have been pulled outside of the .wrapper and .inner classes. Before we wanted the header and footer to shift with the content as the menu opened. Now we want them to always be in place. Only the "panels," our content will slide on and off screen. Since our buttons will remain in place we can also remove the close link/label.

Inside the header we now have a div that contains three labels, one each for each of our three panels. All have the same .btn class we've used before, though they now have their own specific class as well. Notice that each label is for a different toggle or radio button. The three radio inputs are outside the header. They share the name "panel" and each have a unique id as you would expect with radio buttons. They're outside the header so they can be siblings of the .wrapper div.

<header>
  <div id="panel-nav">
    <label class="btn btn-1" for="toggle-1" onclick>Menu</label>
    <label class="btn btn-2" for="toggle-2" onclick>Content</label>
    <label class="btn btn-3" for="toggle-3" onclick>More</label>
  </div>
</header>
		
<input id="toggle-1" name="panel" type="radio" />
<input id="toggle-2" name="panel" type="radio" />
<input id="toggle-3" name="panel" type="radio" />

<div class="wrapper">
  <div class="inner">

    <nav>
      <ul id="nav">
        <li><a href="">Back to Post</a></li>
        <li><a href="sidebar-nav.html">Sidebar Nav</a></li>
        <li><a href="sidebar-nav-js.html">Sidebar Nav JS</a></li>
        <li class="current><a href="panels.html">Panels</a></li>
        <li"><a href="sidebar+.html">Sidebar+</a></li>
      </ul>
    </nav>

    <div class="container main-content">
      <div id="content"></div>
    </div>

    <div id="sidebar"></div>
  </div>
</div>

<section class="subfooter"></section>
<div id="footer"></div>
The 3 panel buttons on small screens

Step 2: The Default CSS

Some of the default css, such as that which styles the buttons is the same as it was above, in fact it's the same as it was for the last article. Similar for the basic look of the menu itself with the gradient background. I won't display either below. Instead the css below are for things that have changed a little more or are new to this pattern.

Naturally we have to hide the radio inputs and we'll do this the same way we have in the past.

#toggle-1, #toggle-2, #toggle-3 {
  position: absolute;
  left: -999em;
}

Now that we've placed all three of our panels inside the same containers, we'll float each one to the left to position them. Each is also given a width 1/3 of the whole. Why will become clear in a moment.

nav {
  float: left;
  padding: 0;
  background: #3b3736;
  width: 33.334%
}

.main-content {
  float: left;
  width: 33.333%
}

#sidebar {
  float: left;
  padding: 0 2%;
  width: 33.333%
}

Why those 33.+% widths? The way this pattern works is seen in the two selectors below. We set the wrapper to 100% width and then hide any overflow content inside it. We set the width of the .inner div to 300% (1/3 of which is 100%) and by default set it's left margin to -100% to the middle panel is visible.

Since overflow: hidden is set, only 1/3 the width of the .inner div will be visible at one time. 1/3 of this width is the width of any of the three panels. In other words only one panel can be visible and all of that panel will be visible.

.wrapper {
  width: 100%;
  overflow: hidden;
}

.inner {
  width: 300%;
  margin-left: -100%;

  -webkit-transition: margin 0.5s;
     -moz-transition: margin 0.5s;
      -ms-transition: margin 0.5s;
       -o-transition: margin 0.5s;
          transition: margin 0.5s;
}

Finally, since the .inner div is the one that will be moving, we'll add the transition here and as you can see this transition will only occur when there's a change in the margin of the .inner div.

The menu panel visible by on small screens

Step 3: The CSS to Toggle the Panels

Switching panels is remarkably easy. The work was really in the set up above. All we need to do to show a different panel is to adjust the left margin of the .inner div. We need to be able to access the .inner div based on which radio button is selected so instead of using the generic :checked, we'll use it attached to the individual radio buttons.

The sidebar panel visible by on small screens

#toggle-1 is for the menu, #toggle-2 is for the main content, and #toggle-3 is for the sidebar. Since everything is floated left a margin-left of 0 will show the first of these panels or the menu. A left margin of -100% pushes the menu off screen and displays the main content. A left margin of -200% pushes both the menu and main content off screen left and displays the sidebar.

#toggle-1:checked ~ .wrapper .inner {
  margin-left: 0%;
}

#toggle-2:checked ~ .wrapper .inner {
  margin-left: -100%;
}

#toggle-3:checked ~ .wrapper .inner {
  margin-left: -200%;
}
The menu and content panels visible by on medium screens

Step 4: The CSS in Media Queries

At 48em we have enough room to show more than one panel, though it's too narrow to show all three. We have our choice of which two adjacent panels to show. I thought showing the menu and the main content by default made the most sense.

Because our content will always show at this width, we can remove the display of it's radio button. And while we're at it, let's move the two remaining buttons up and to the right. While I'm not showing it below I also tweaked the position of logo and added some padding to the header.

@media screen and (min-width: 48em) {
  #panel-nav {
    float: right;
    margin: 1.75em 3.25% 0 0;
  }

  .btn {
    padding: 0.45em 0.9em;
    margin-left: 0.1em;
  }		

  .btn-2 {
    display: none;
  }
}
The content and sidebar panels visible by on medium screens

The general idea for showing the panels is the same as above, though we'll have to change the numbers to show more than one panel at a time. Just like the default we want the .inner div's width to be greater than 100% so that some of it will remain hidden. Unlike above we now want 2/3 of what's inside to show instead of only 1/3.

We want the .inner div to be 1.5 times (3/2) the width of the .wrapper div and so we set it to be 150%. At smaller widths we wanted 1/3 of the overall .inner width to be visible at any time. Now we want 2/3 so the two panels showing at any one time should have widths that sum to about 67%.

The numbers I used are 25% each for the menu and sidebar and 40% for the main content. 40% + 25% = 65%, which is close enough here, given small amounts of padding exist in elements in this demo not shown in the tutorial. The idea though is the total for the two panels showing would be close to 67%.

Since we no longer want to show the gradient background behind the menu, the background is set to be transparent. Also the sidebar will need some top margin so it sits evenly with the main content.

Controlling which panels show is again dependent on which radio button is currently selected. We only need two states now. If either the menu button or the, now hidden, content button are checked the .inner div will have 0 left margin. Setting this margin to -37% will show the main content and the sidebar.

@media screen and (min-width: 48em) {
  .inner {
    width: 150%;
    margin-left: 0;
  }
	
  nav {
    width: 25%;
    background: none;
  }

  .main-content {
    width: 40%;
  }
	
  #sidebar {
    width: 25%;
    margin-top: 3em;
  }

  #toggle-1:checked ~ .wrapper .inner {
    margin-left: 0%;
  }
	
  #toggle-2:checked ~ .wrapper .inner {
    margin-left: 0%;
  }
	
  #toggle-3:checked ~ .wrapper .inner {
    margin-left: -37%;
  }
}

One downside to using radio buttons is that once one is selected, one always has to be selected. What this means is that if the more button is selected to show the sidebar, the only way to bring back the menu is to select its radio button. Clicking More again will do nothing. We'll correct this in the next pattern, however it's something to consider when using radio buttons. When using radio buttons you may never want to show either one panel or all panels and nothing in between.

All 3 panels visible by default on wide screens

At 75em we can show all three panels at once. We no longer need to show any buttons and so set the display of all of them to none.

We now want to show the entire contents of the .inner div and so set it's width to 100%. Nothing needs to be hidden any longer. We'll also turn off the transition since it's no longer needed.

The widths and other spacing for the three panels is arbitrary. I used my eye to determine how wide I wanted each to be in the layout. Nothing is sacred about any of the numbers here, though ideally in a real project there would be some logic behind where each panel is located in the layout.

One last change is to make sure that the margin-left of the .inner div is always 0. Just in case someone resizes their browser after having the sidebar open we should reset this state.

@media screen and (min-width: 75em) {
  .btn {display: none;}

  .inner {
    width: 100%;

    -webkit-transition: 0;
       -moz-transition: 0;
        -ms-transition: 0;
         -o-transition: 0;
            transition: 0;
    }

  nav {
    width: 20%;
  }

  .main-content {
    width: 53%;
    padding: 0 2% 0 0;
  }

  #sidebar {
    width: 23%;
    padding: 0 1%;
  }

  #toggle-3:checked ~ .wrapper .inner {
    margin-left: 0%;
  }
}

@media screen and (min-width: 80em) {
  .logo {margin-left: -1.4%}
}

Thoughts

There are a few things to consider with this pattern. While here we have three panels, you could have as many as you want. You'll need to change the math for the widths and margins with a different number of panels, but the general idea will be the same.

If you've checked the demo with your screen open to less 48em you might notice than when the menu or sidebar is displaying there's a lot of empty space below either and the footer. The height of the .inner div is controlled here by the main content panel, since it holds the most content. That suggests this pattern probably works best when each panel contains a similar amount of content or you don't mind a lot of empty space below some of the panel content.

Another thing to consider is that while we used radio buttons here, you could always use Javascript. Instead of all the :checked selectors you could add and remove classes to each panel the same way we did in the sidebar nav js pattern above. We'll use jQuery again in the next pattern to allow the panels to work independently of each other.

Examples

Below are three demos that make use of the panel pattern. All three are using Javascript for the click events so you can compare them to the demo here. Also these demos only show one panel at a time regardless of how wide the browser is open.

The Sidebar+ Pattern

This pattern combines some of the things from the patterns above. In some respects it acts like the panel pattern in that clicking a button will call that "panel." It acts like the sidebar pattern in the sense that panels can be open and closed independently of other panels.

Approach: On the smallest screens only the main content will show by default. Our header will contain 2 buttons, one each for the menu and the sidebar. Clicking either button opens that panel and clicking it again closes that panel. If either panel is open, clicking the other button will close the open panel and open the closed one. If we were using css click events we'd want something that works like radio buttons sometimes and a checkbox at other times. Instead we'll expand what should now be a familiar jQuery function to control the click events.

Step 1: The HTML

The html is almost identical to what we saw in the panels pattern. Once again all three panels of information are included inside the .wrapper and .inner divs. Since we're using Javascript for the click events we'll remove the radio inputs and convert the labels to links.

We only need two buttons instead of the three we used with the panel pattern so we'll remove one and place the remaining two on either site of the logo.

<header>
  <div class="container">
    <a class="btn btn-1">Menu</a>
    <img class="logo" src="images/logo.png" width="252" height="46" />
    <a class="btn btn-2">More</a>
  </div>
</header>

<div class="wrapper">
  <div class="inner">

    <nav>
      <ul id="nav">
        <li><a href="">Back to Post</a></li>
        <li><a href="sidebar-nav.html">Sidebar Nav</a></li>
        <li><a href="sidebar-nav-js.html">Sidebar Nav JS</a></li>
        <li class="current"><a href="sidebar+.html">Sidebar+</a></li>
        <li><a href="panels.html">Panels</a></li>
      </ul>
    </nav>

    <div class="container main-content">
      <div id="content"></div>
    </div>

    <div id="sidebar"></div>
  </div>
</div>

<section class="subfooter"></section>
<div id="footer"></div>
The content panel visible by default on small screens

Step 2: The Default CSS

The default css is nearly identical to what was in the panels pattern. Naturally we no longer need to hide the radio inputs and we can remove one of the buttons. The two remaining buttons are positioned absolutely, one to the left and one to the right. Otherwise they've been styled the same way all buttons in this tutorial have been styled.

.btn-1 {
	position: absolute;
	top: 1.5em;
	left: 5%;
}

.btn-2 {
	position: absolute;
	top: 1.5em;
	right: 5%;
}

Since the css here is the same you can safely assume we'll be using the same general method to slide "panels" on and off the screen. Once again the .wrapper div is set to 100% width with its overflow hidden. The width of the .inner div is set to 300% and its left margin is set -100% to center it.

While not shown below each "panel" inside has a width 1/3 the total width of the .inner div.

.wrapper {
	width: 100%;
	overflow: hidden;
}

.inner {
	width: 300%;
	margin-left: -100%;

	-webkit-transition: margin 0.5s;
	   -moz-transition: margin 0.5s;
	    -ms-transition: margin 0.5s;
	     -o-transition: margin 0.5s;
	        transition: margin 0.5s;
}
The menu panel visible on small screens

Step 3: The CSS to Toggle the Menu and Sidebar

As we're using the same general method to slide the different parts of the layout on and off the screen you can guess it'll be just as simple as it was with the panels pattern. If anything it's easier since we only have two buttons instead of three.

The sidebar panel visible on small screens

The difference is instead of checked radio buttons we're adding and removing classes. Here I've added them to the .inner div. Note that each "panel" gets its own unique class, which is what lets us control them independently.

.inner.menu {
  margin-left: 0;
}

.inner.more {
  margin-left: -200%;	
}
The menu and content panels visible on medium screens

Step 4: The CSS In Media Queries

We're doing the same thing here as we did with the panels pattern. At 48em we'll show both the menu and the main content by default. Other than changing #toggle-1:checked ~ .wrapper .inner to .inner.menu and #toggle-3:checked ~ .wrapper .inner to .inner.more everything is identical. The same values for the different widths and margins are used.

@media screen and (min-width: 48em) {
  nav {
    width: 25%;
    background: none;
  }

  .inner {
    width: 150%;
    margin-left: 0;
  }

  .main-content {
    width: 40%;
  }
	
  .container {
    padding: 0 2%;
  }
	
  #content {
    width: 100%;
  }
	
  #sidebar {
    width: 25%;
    margin-top: 3em;
  }
	
  .inner,
  .inner.menu {
    margin-left: 0%;
  }
	
  .inner.more {
    margin-left: -37%;
  }
}
The content and sidebar panels visible on medium screens

At 75em we'll show all three columns at once and remove the buttons. Again it's nearly identical other than the changed from :checked to class so I won't present it all here.

I would like to point out something we're doing here that you may or may not decide to include. Between 48em and 75em we're showing the menu and content by default with the sidebar off to the right. We could remove the menu button and simply leave the More button and let it open and close the sidebar.

Instead I left the Menu button, which only does something if the sidebar is present and the menu is off screen left. This could potentially be confusing in the default state, since it could lead visitors to think clicking it will reveal an additional menu from the one shown. The tradeoff is that if someone is viewing the page below 48em and has the sidebar open and then increases their browser to 48em wide or above there's likely less confusion for how to bring the menu back.

The latter case might not seem likely, but it seemed reasonable to me if someone flips a small tablet from portrait to landscape mode so I left the button in. It is something to consider.

All 3 panels visible on wide screens

Step 5: The JavaScript

Hopefully the jQuery below is becoming familiar. Once again we'll intercept the click of either button and navigate the DOM to add or remove a class to the .inner div. With both buttons we need to move up a couple of containers to get to the header and so we'll use the parents() function. From there the .wrapper div is a sibling.

The .inner div is inside the .wrapper div and we can find it using the aptly named find() function.

The buttons control different panels so we do need to add and remove different classes. .btn-1 is tied to the menu. If the menu class is present on .inner we'll remove it. If it's not present we'll add it. Because the possibility exists that .more is present when .menu isn't we'll close that too.

For .btn-2 we do the same, except we reverse the menu and more classes.

$(document).ready(function() {
  $('a.btn-1').click(function() {
    if($(this).parents('header').siblings('.wrapper').find('.inner').hasClass('menu')){
      $(this).parents('header').siblings('.wrapper').find('.inner').removeClass('menu');
    } else {
      $(this).parents('header').siblings('.wrapper').find('.inner').addClass('menu');
      $(this).parents('header').siblings('.wrapper').find('.inner').removeClass('more');
    }
    return false;
  });

  $('a.btn-2').click(function() {
    if($(this).parents('header').siblings('.wrapper').find('.inner').hasClass('more')){
      $(this).parents('header').siblings('.wrapper').find('.inner').removeClass('more');
    } else {
      $(this).parents('header').siblings('.wrapper').find('.inner').addClass('more');
      $(this).parents('header').siblings('.wrapper').find('.inner').removeClass('menu');
    }
    return false;
  });
});

Thoughts

What's nice about this pattern is that each "panel" of content can be controlled independently and yet when one is already open a single button can both close it and open the other "panel."

However this pattern isn't really meant for a series of panels. It'll work best when you only have one piece of content off canvas on either side. You could also include a button that includes content that slides in from the top and another that slides content in from the bottom. More than one "panel" on any of the four sides is probably not a good idea with this pattern.

Examples

Below are some examples of the sidebar+ nav pattern, where both sidebars open and close independent of each other. Note how the middle two examples have one sidebar sliding in from the top and a second on in from the side.

Summary

As we've seen from the first three tutorials in the series the patterns here are all patterns on a theme. With off canvas patterns some content is on screen and some off screen until requested. Here I only showed content off to the left or right, but the content could just as easily be off the top or bottom of the screen.

They all generally work the same. Off canvas content is placed off canvas usually through margins. When the content is positioned a left or right (or top or bottom) value can be adjusted to place the canvas off screen. Clicking different buttons adjusts these values to present different content on the screen and move other content off the screen.

These off canvas patterns are the last of the responsive patterns in this series. I hope you enjoyed is and the whole series. I didn't present every possible combination for where navigation or panels of content can move of hide or rearrange themselves. I tried instead to present all the basic methods for hiding and moving elements in the hopes that they would be easy enough to understand and you could take it from here.

We've rearranged menu items at the top of the page, hidden some while showing others, and converting a list of links into a select menu. We've fixed our menu to the bottom of the page and clicked to quickly get to it. We hid menus and revealed them in different ways on button clicks. And finally we placed menus and other content off canvas until we requested them.

We've also learned three different techniques to control click events using css pseudo selectors, the :target hack, the :checkbox hack, and the radio button hack. When css wasn't right we used jQuery to add and remove classes that represent the different states.

We also saw a lot of examples showing variations of all these patterns in demos and on live sites.

I hope you've enjoyed this tutorial, with any luck you'll feel more comfortable experimenting with these patterns and using them on live sites.

Advertisement