Creating Off Screen Menus That Transition Into View Using CSS3

In this tutorial, we’re going to create some slide and push menus using CSS3 transitions. The menus will be hidden off screen at first, and will transition into view when the “toggle menu” button is pressed.

Let’s first define the difference in the two types of menus:

  • A slide menu will slide in above the content when toggled, and the content will stay in place.
  • A push menu will slide in and “push” the main content aside when toggled.

Before we proceed, there are some important things to note:

  • This tutorial relies on CSS transitions to work smoothly. On older browsers though, the content will just jump to the appropriate position.
  • I’m using classie.js for easily adding and removing classes.
  • I’m using the JavaScript functions querySelector and querySelectorAll which are supported from IE8 and up.

Finally, let’s take a look at the 8 varieties of menu we will create:

  • Slide left menu, which slides in from the left above the content
  • Slide right menu, which slides in from the right above the content
  • Slide top menu, which slides in from the top above the content
  • Slide in bottom menu, which slides in from the bottom above the content
  • Push left menu, which slides in from the left and pushes the content to the right
  • Push right menu, which slides in from the right and pushes the content to the left
  • Push top menu, which slides in from the top and pushes the content down
  • Push bottom menu, which slides in from the bottom and pushes the content up

When a menu is open, we’ll show a “mask” over the main wrapper. This is basically a semi-transparent overlay that hides the main content. When the user clicks the overlay, the menu will toggle back out of view. In each menu, there will also be a “close menu” button, which will help out when the menu takes up the full width of the screen on smaller screen sizes. Let’s first take a look at the general markup and CSS for all.

The HTML

<body>

    <nav class="menu slide-menu-left">
        <ul>
            <li><button class="close-menu">← Close</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /slide menu left -->

    <nav class="menu slide-menu-right">
        <ul>
            <li><button class="close-menu">Close →</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /slide menu right -->

    <nav class="menu slide-menu-top">
        <ul>
            <li><button class="close-menu">↑ Close</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /slide menu top -->

    <nav class="menu slide-menu-bottom">
        <ul>
            <li><button class="close-menu">Close ↓</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /slide menu bottom -->

    <nav class="menu push-menu-left">
        <ul>
            <li><button class="close-menu">← Close</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /push menu left -->

    <nav class="menu push-menu-right">
        <ul>
            <li><button class="close-menu">Close →</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /push menu right -->

    <nav class="menu push-menu-top">
        <ul>
            <li><button class="close-menu">↑ Close</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /push menu top -->

    <nav class="menu push-menu-bottom">
        <ul>
            <li><button class="close-menu">Close ↓</button></li>
            <li><a href="#">Broccoli</a></li>
            ...
        </ul>
    </nav><!-- /push menu bottom -->

    <div id="wrapper">
        <div id="main">
            <div class="container">
                <div class="buttons">
                    <button class="nav-toggler toggle-slide-left">Slide Menu Left</button>
                    ...
                </div><!-- /buttons -->
                <section class="content">
                    <h1>Vegetables</h1>
                    <p>Turnip greens yarrow...</p>
                </section><!-- /.content -->
            </div>
        </div><!-- #main -->

    </div><!-- /#wrapper -->

</body>

The Common CSS

/* ------------------------------------------------------------ *\
|* ------------------------------------------------------------ *|
|* Template
|* ------------------------------------------------------------ *|
\* ------------------------------------------------------------ */
body {
    overflow-x: hidden
}
#wrapper {
    position: relative;
    z-index: 10;
    top: 0;
    left: 0;
    -webkit-transition: all 0.3s;
    -moz-transition: all 0.3s;
    -ms-transition: all 0.3s;
    -o-transition: all 0.3s;
    transition: all 0.3s;
}
section {
    margin-bottom: 30px
}
section h1 {
    font-family: "Oswald", sans-serif;
    margin-bottom: 10px;
}
section p {
    margin-bottom: 30px
}
section p:last-child {
    margin-bottom: 0
}
section:last-child {
    margin-bottom: 0
}
section.toggle {
    text-align: center
}
.mask {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 15;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.8);
}
/* ------------------------------------------------------------ *\
|* ------------------------------------------------------------ *|
|* Menus
|* ------------------------------------------------------------ *|
\* ------------------------------------------------------------ */
/* general style for all menus */
nav.menu {
    position: fixed;
    z-index: 20;
    background-color: #67b5d1;
    overflow: hidden;
    -webkit-transition: all 0.3s;
    -moz-transition: all 0.3s;
    -ms-transition: all 0.3s;
    -o-transition: all 0.3s;
    transition: all 0.3s;
}
nav.menu ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
}
nav.menu a {
    font-weight: 300;
    color: #fff;
}
button.close-menu {
    background-color: #3184a1;
    color: #fff;
}
button.close-menu:focus {
    outline: none
}

Understanding The Structure

Our body overflow-x is set to hidden, because we don’t want to have scrollbars on display when the wrapper is pushed to the left or right. When it’s pushed vertically, it doesn’t matter. More importantly though is positioning our off-screen navigation menus OUTSIDE the wrapper, because oddly enough, when a transform is applied to an element, it takes on a relative positioning temporarily. Since our navigation menus need to be fixed to the outer parts of the browser window, we don’t want it inside an element with relative positioning. Inside each menu, there’s a list of menu items, and a close menu button.

There’s ongoing discussion about the performance differences of absolute positioning/left/top versus transforms/translating. Paul Irish digs deep into this and you can read about it here. I’m using positioning in this case because we will have a non-transitioning fallback solution for older browsers, without having to use conditional stylesheets. Also, after testing both, the difference was totally unseeable to the human eye. Let’s take a look at version of our menu now.

1) Slide Menu Left

Slide Left Menu css3 transition app-style menu navigation

This menu slides in from the left over the content. Here’s the CSS for it:

nav.slide-menu-left {
    top: 0;
    width: 300px;
    height: 100%;
}
nav.slide-menu-left li {
    display: block;
    text-align: center;
    border-bottom: solid 1px #3184a1;
    border-top: solid 1px #b5dbe9;
}
nav.slide-menu-left li:first-child {
    border-top: none
}
nav.slide-menu-left li:last-child {
    border-bottom: none
}
nav.slide-menu-left a {
    display: block;
    padding: 10px;
    font-size: 18px;
}
nav.slide-menu-left button.close-menu {
    margin: 10px 0;
    padding: 10px 30px;
    background-color: #3184a1;
    color: #fff;
}
nav.slide-menu-left {
    left: -300px
}
body.sml-open nav.slide-menu-left {
    left: 0
}

2) Slide Menu Right

Slide Menu Right css3 transition app-style menu navigation

This menu slides in from the right above the content. Here’s the CSS for it:

nav.slide-menu-right {
    top: 0;
    width: 300px;
    height: 100%;
}
nav.slide-menu-right li {
    display: block;
    text-align: center;
    border-bottom: solid 1px #3184a1;
    border-top: solid 1px #b5dbe9;
}
nav.slide-menu-right li:first-child {
    border-top: none
}
nav.slide-menu-right li:last-child {
    border-bottom: none
}
nav.slide-menu-right a {
    display: block;
    padding: 10px;
    font-size: 18px;
}
nav.slide-menu-right button.close-menu {
    margin: 10px 0;
    padding: 10px 30px;
    background-color: #3184a1;
    color: #fff;
}
nav.slide-menu-right {
    right: -300px
}
body.smr-open nav.slide-menu-right {
    right: 0
}

3) Slide Menu Top

Slide Top Menu css3 transition app-style menu navigation

This menu slides in from the top above the content. Here’s the CSS:

nav.slide-menu-top {
    left: 0;
    width: 100%;
    height: 100px;
}
nav.slide-menu-top ul {
    text-align: center;
    padding: 25px 0 0 0;
}
nav.slide-menu-top li {
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}
nav.slide-menu-top a {
    display: block;
    line-height: 50px;
    padding: 0 10px;
    font-size: 18px;
}
nav.slide-menu-top button.close-menu {
    display: block;
    line-height: 50px;
    margin: 0;
    padding: 0 10px;
}
nav.slide-menu-top {
    top: -100px
}
body.smt-open nav.slide-menu-top {
    top: 0
}

4) Slide Menu Bottom

Slide  Bottom Menu css3 transition app-style menu navigation

This menu slides in from the bottom above the content. Here’s the CSS:

nav.slide-menu-bottom {
    left: 0;
    width: 100%;
    height: 100px;
}
nav.slide-menu-bottom ul {
    text-align: center;
    padding: 25px 0 0 0;
}
nav.slide-menu-bottom li{
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}
nav.slide-menu-bottom a{
    display: block;
    line-height: 50px;
    padding: 0 10px;
    font-size: 18px;
}
nav.slide-menu-bottom button.close-menu{
    display: block;
    line-height: 50px;
    margin: 0;
    padding: 0 10px;
}
nav.slide-menu-bottom {
    bottom: -100px
}
body.smb-open nav.slide-menu-bottom {
    bottom: 0
}

5) Push Menu Left

Push Menu Left css3 transition app-style menu navigation

This menu slides in from the left, and pushes the content to the right. A lot of the CSS is similar to the slide menu left, except we have to move the wrapper as well. Here’s the CSS:

nav.push-menu-left {
    top: 0;
    width: 300px;
    height: 100%;
}
nav.push-menu-left li {
    display: block;
    text-align: center;
    border-bottom: solid 1px #3184a1;
    border-top: solid 1px #b5dbe9;
}
nav.push-menu-left li:first-child {
    border-top: none
}
nav.push-menu-left li:last-child {
    border-bottom: none
}
nav.push-menu-left a {
    display: block;
    padding: 10px;
    font-size: 18px;
}
nav.push-menu-left button.close-menu {
    margin: 10px 0;
    padding: 10px 30px;
    background-color: #3184a1;
    color: #fff;
}
nav.push-menu-left {
    left: -300px
}
body.pml-open nav.push-menu-left {
    left: 0
}
body.pml-open #wrapper {
    left: 300px
}

6) Push Menu Right

Push Menu Right css3 transition app-style menu navigation

This menu slides in from the right, and pushes the content to the left. A lot of the CSS is similar to the slide menu right, except we have to move the wrapper as well. Here’s the CSS:

nav.push-menu-right {
    top: 0;
    width: 300px;
    height: 100%;
}
nav.push-menu-right li {
    display: block;
    text-align: center;
    border-bottom: solid 1px #3184a1;
    border-top: solid 1px #b5dbe9;
}
nav.push-menu-right li:first-child {
    border-top: none
}
nav.push-menu-right li:last-child {
    border-bottom: none
}
nav.push-menu-right a {
    display: block;
    padding: 10px;
    font-size: 18px;
}
nav.push-menu-right button.close-menu {
    margin: 10px 0;
    padding: 10px 30px;
    background-color: #3184a1;
    color: #fff;
}
nav.push-menu-right {
    right: -300px
}
body.pmr-open nav.push-menu-right {
    right: 0
}
body.pmr-open #wrapper {
    left: -300px
}

7) Push Menu Top

Push Menu Top css3 transition app-style menu navigation

This menu slides in from the top, and pushes the content downward. A lot of the CSS is similar to the slide menu top, except we have to move the wrapper as well. Here’s the CSS:

nav.push-menu-top {
    left: 0;
    width: 100%;
    height: 100px;
}
nav.push-menu-top ul {
    text-align: center;
    padding: 25px 0 0 0;
}
nav.push-menu-top li {
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}
nav.push-menu-top a {
    display: block;
    line-height: 50px;
    padding: 0 10px;
    font-size: 18px;
}
nav.push-menu-top button.close-menu {
    display: block;
    line-height: 50px;
    margin: 0;
    padding: 0 10px;
}
nav.push-menu-top {
    top: -100px
}
body.pmt-open nav.push-menu-top {
    top: 0
}
body.pmt-open #wrapper {
    top: 100px
}

8) Push Menu Bottom

Push Menu Bottom css3 transition app-style menu navigation

This menu slides in from the botomm, and pushes the content upward. A lot of the CSS is similar to the slide menu bottom, except we have to move the wrapper as well. Here’s the CSS:

nav.push-menu-bottom {
    left: 0;
    width: 100%;
    height: 100px;
}
nav.push-menu-bottom ul {
    text-align: center;
    padding: 25px 0 0 0;
}
nav.push-menu-bottom li {
    display: inline-block;
    margin: 0;
    vertical-align: middle;
}
nav.push-menu-bottom a {
    display: block;
    line-height: 50px;
    padding: 0 10px;
    font-size: 18px;
}
nav.push-menu-bottom button.close-menu {
    display: block;
    line-height: 50px;
    margin: 0;
    padding: 0 10px;
}
nav.push-menu-bottom {
    bottom: -100px
}
body.pmb-open nav.push-menu-bottom {
    bottom: 0
}
body.pmb-open #wrapper {
    top: -100px
}

The JavaScript

Now, let’s take a look at the JavaScript to toggle our classes when we hit the different menu buttons. We’ll also display our mask over the rest of the content, and implement some “close menu” functionality when the user clicks the mask or the close-menu button. Remember, I’m using classie.js to add and remove classes. My JavaScript, of course, is covering all eight menus. You might want to tailor your JavaScript and cut out the unnecessary stuff (unless you’re actually using 8 menus on your site…). Here’s the JavaScript:

(function( window ){

	'use strict';

	var body = document.body,
		mask = document.createElement("div"),
		toggleSlideLeft = document.querySelector( ".toggle-slide-left" ),
		toggleSlideRight = document.querySelector( ".toggle-slide-right" ),
		toggleSlideTop = document.querySelector( ".toggle-slide-top" ),
		toggleSlideBottom = document.querySelector( ".toggle-slide-bottom" ),
		togglePushLeft = document.querySelector( ".toggle-push-left" ),
		togglePushRight = document.querySelector( ".toggle-push-right" ),
		togglePushTop = document.querySelector( ".toggle-push-top" ),
		togglePushBottom = document.querySelector( ".toggle-push-bottom" ),
		slideMenuLeft = document.querySelector( ".slide-menu-left" ),
		slideMenuRight = document.querySelector( ".slide-menu-right" ),
		slideMenuTop = document.querySelector( ".slide-menu-top" ),
		slideMenuBottom = document.querySelector( ".slide-menu-bottom" ),
		pushMenuLeft = document.querySelector( ".push-menu-left" ),
		pushMenuRight = document.querySelector( ".push-menu-right" ),
		pushMenuTop = document.querySelector( ".push-menu-top" ),
		pushMenuBottom = document.querySelector( ".push-menu-bottom" ),
		activeNav
	;
	mask.className = "mask";

	/* slide menu left */
	toggleSlideLeft.addEventListener( "click", function(){
		classie.add( body, "sml-open" );
		document.body.appendChild(mask);
		activeNav = "sml-open";
	} );

	/* slide menu right */
	toggleSlideRight.addEventListener( "click", function(){
		classie.add( body, "smr-open" );
		document.body.appendChild(mask);
		activeNav = "smr-open";
	} );

	/* slide menu top */
	toggleSlideTop.addEventListener( "click", function(){
		classie.add( body, "smt-open" );
		document.body.appendChild(mask);
		activeNav = "smt-open";
	} );

	/* slide menu bottom */
	toggleSlideBottom.addEventListener( "click", function(){
		classie.add( body, "smb-open" );
		document.body.appendChild(mask);
		activeNav = "smb-open";
	} );

	/* push menu left */
	togglePushLeft.addEventListener( "click", function(){
		classie.add( body, "pml-open" );
		document.body.appendChild(mask);
		activeNav = "pml-open";
	} );

	/* push menu right */
	togglePushRight.addEventListener( "click", function(){
		classie.add( body, "pmr-open" );
		document.body.appendChild(mask);
		activeNav = "pmr-open";
	} );

	/* push menu top */
	togglePushTop.addEventListener( "click", function(){
		classie.add( body, "pmt-open" );
		document.body.appendChild(mask);
		activeNav = "pmt-open";
	} );

	/* push menu bottom */
	togglePushBottom.addEventListener( "click", function(){
		classie.add( body, "pmb-open" );
		document.body.appendChild(mask);
		activeNav = "pmb-open";
	} );

	/* hide active menu if mask is clicked */
	mask.addEventListener( "click", function(){
		classie.remove( body, activeNav );
		activeNav = "";
		document.body.removeChild(mask);
	} );

	/* hide active menu if close menu button is clicked */
	[].slice.call(document.querySelectorAll(".close-menu")).forEach(function(el,i){
		el.addEventListener( "click", function(){
			classie.remove( body, activeNav );
			activeNav = "";
			document.body.removeChild(mask);
		} );
	});

})( window );

Wrap Up

There it is! Some nice slide and push menus, ready to drop into your project. I included sample media queries in the source code, so feel free to download it or view the demos by clicking the links below.

(10 Posts)

I'm a web designer & developer from Trinidad & Tobago, with a degree in Mechanical Engineering. I love the logical side of the web, and I'm an artist/painter at heart. I endorse progressive web techniques, and try to learn something every day. I try to impart my knowledge as much as possible on my personal blog, callmenick.com. I love food, I surf every weekend, and I have an amazing creative partnership with fellow mischief maker Elena. Together, we run SAYSM.

Comments

  • Thanks so much for the feature Speckyboy!

  • David

    Nick, this is a great tutorial and example. I was looking for a better inspiration to showcase my portfolio, and thanks to you, I now have bigger ideas and inspiration. Just checked out our site, too. That rocks. Thanks for showcasing your work!

    David

  • I really thank you for a great tut. I was really looking for this since months, and tried many fiddles on my own , just for the mask effect. I succeeded in creating a push menu, but without mask effect and now you have filled the gap. Thanks again, keep up the great work. I have a doubt, can i add animation effects for the push menu , like bounceback with easing etc. If so i would be grateful to know more. regards

  • Great tutorial. Ideally I’d like the left and right menus to be open on larger screens, and have them collapse on smaller screens. How would you do this? It would also be very important for the menus to scroll if the content in them is longer than the page height, but they don’t do that now. How would you go about fixing this?

  • Can you explain what you mean by “collapse on smaller screens”? As far as scrolling goes, you might want to consider an overflow property on the container, or use absolute positioning instead of fixed on the menu.

  • Thanks a lot David!

  • Alex

    Any insight into how this might work on mobile apps? Specifically, apps made using PhoneGap? I’ve had issues with slide and push menus within PhoneGap, and am looking for a viable solution. Not sure if this would work though, considering the use of position: fixed and its partial support in many mobile browsers.

  • The good thing about the fixed positioning in this case is that there is no scrolling taking place when the fixed menu flies out. Position:fixed is usually buggy in mobile browsers due to scrolling. If you’re developing with PhoneGap though, then there are options too. You can consider using absolute positioning, setting all containers to 100% width and height, and using the css properties:

    overflow-y: scroll;
    -webkit-overflow-scrolling: touch;

    This way, you have absolute positioning instead of fixed. There’s definitely ways to implement it with absolute positioning, so try some stuff and see what you come up with.

  • Hey the @enviraphani:disqus, sorry for the late reply. You can indeed add effects for the push menu, but you would need to create them using the @keyframe animation syntax. There are many tools out there to help you get started if you don’t want to write your own animation keyframe sequence though. Here’s an example of one – http://daneden.github.io/animate.css/