Google Nexus Website Menu

A tutorial on how to re-create the slide out sidebar menu that can be seen on the Google Nexus 7 website.

Google Nexus Menu

View demo Download source

Today I want to show you how to reconstruct the sidebar menu of the Google Nexus 7 page. It slides out with a really nice effect where some subitems get expanded as well. When hovering over a special menu icon, the sidebar icons will be revealed. When clicking on the icon, the whole sidebar menu will be shown. The first sidebar menu item is a search input which is styled the same way like the other menu items.

We will reconstruct this menu using unordered, nested lists and some CSS transitions. We’ll use JavaScript to apply classes for the opening effects and handling the hover and click events. With the help of a media query, we’ll adjust the size to make sense for smaller devices.

So let’s get started!

The Markup

Our menu will consist of two main parts: a main menu, the one that you can see at the top like a header, and the sidebar menu. We’ll give the class “gn-menu-main” to the first one and wrap the second one in a nav element. You could of course use any structure that you prefer.
The first menu item will contain the menu icon anchor and the nav element:

<ul id="gn-menu" class="gn-menu-main">
	<li class="gn-trigger">
		<a class="gn-icon gn-icon-menu"><span>Menu</span></a>
		<nav class="gn-menu-wrapper">
			<!-- ... -->
		</nav>
	</li>
	<li><a href="http://tympanus.net/codrops">Codrops</a></li>
	<li><!-- ... --></li>
	<!-- ... -->
</ul>

Inside of the nav element we’ll add another wrapper that will help us with hiding the nasty scrollbar for Windows browsers. The heart of this submenu is the unordered list with the class “gn-menu”. It will consist of list items, some of which will have a sublist. The first item will be the special search input:

<div class="gn-scroller">
	<ul class="gn-menu">
		<li class="gn-search-item">
			<input placeholder="Search" type="search" class="gn-search">
			<a class="gn-icon gn-icon-search"><span>Search</span></a>
		</li>
		<li>
			<a class="gn-icon gn-icon-download">Downloads</a>
			<ul class="gn-submenu">
				<li><a class="gn-icon gn-icon-illustrator">Vector Illustrations</a></li>
				<li><a class="gn-icon gn-icon-photoshop">Photoshop files</a></li>
			</ul>
		</li>
		<li><a class="gn-icon gn-icon-cog">Settings</a></li>
		<li><!-- ... --></li>
		<!-- ... -->
	</ul>
</div><!-- /gn-scroller -->

Now, let’s style everything.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.

Let’s start by setting border-box for all the box-sizing:

*,
*:after,
*::before {
    box-sizing: border-box;
}

Since we’ll be using an icon font for the icons, we’ll head over to IcoMoon and select some nice icons from Matthew Skiles’ Eco Ico set.

@font-face {
    font-weight: normal;
    font-style: normal;
    font-family: 'ecoicons';
    src: url("../fonts/ecoicons/ecoicons.eot");
    src: url("../fonts/ecoicons/ecoicons.eot?#iefix") format("embedded-opentype"), url("../fonts/ecoicons/ecoicons.woff") format("woff"), url("../fonts/ecoicons/ecoicons.ttf") format("truetype"), url("../fonts/ecoicons/ecoicons.svg#ecoicons") format("svg");
}

Later we’ll use a pseudo element to add the icons to the anchors.

But let’s style all the lists before:

.gn-menu-main,
.gn-menu-main ul {
    margin: 0;
    padding: 0;
    background: white;
    color: #5f6f81;
    list-style: none;
    text-transform: none;
    font-weight: 300;
    font-family: 'Lato', Arial, sans-serif;
    line-height: 60px;
}

These are some general (reset) styles for the lists and the sublists.

Now, let’s specify the styles for the main list. It will be fixed to the top of the page and we’ll give it a height of 60 pixels:

.gn-menu-main {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 60px;
    font-size: 13px;
}

The general style for all the links in our menu and submenus will be the following:

.gn-menu-main a {
    display: block;
    height: 100%;
    color: #5f6f81;
    text-decoration: none;
    cursor: pointer;
}

Let’s also define some hover styles where we invert the colors. For the first sidebar menu item which will have a search input, we’ll need a special hover style. There we won’t have an anchor that fills all the item, so let’s define the hover on the li and control what happens to the icon (the anchor) and the li itself:

.no-touch .gn-menu-main a:hover,
.no-touch .gn-menu li.gn-search-item:hover,
.no-touch .gn-menu li.gn-search-item:hover a {
    background: #5f6f81;
    color: white;
}

The list item childen will float left and they’ll have a right border:

.gn-menu-main > li {
    display: block;
    float: left;
    height: 100%;
    border-right: 1px solid #c6d0da;
    text-align: center;
}

The first list item will be the special trigger item and because we will hide the text and use a pseudo element for the menu icon, we will set the user-select to none and the width to be the same as the items’ height.

.gn-menu-main li.gn-trigger {
    position: relative;
    width: 60px;
    user-select: none;
}

The last item in our main list will be floated right and we’ll swap the border:

.gn-menu-main > li:last-child {
    float: right;
    border-right: none;
    border-left: 1px solid #c6d0da;
}

The anchors for the main menu will have some padding and we’ll style the text a bit differently:

.gn-menu-main > li > a {
    padding: 0 30px;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: bold;
}

Let’s clear the floats with the following micro clearfix hack by Nicolas Gallagher:

.gn-menu-main:after {
    display: table;
    clear: both;
    content: '';
}

Alright, so now it just misses the style of the menu icon, but let’s leave that one for later when we define the other icon pseudo classes.

Let’s move on to the wrapper for the sidebar menu. Why do we need those extra wrappers? Well, if you don’t mind having a scrollbar visible you might as well get rid of them and simply set the menu to overflow-y: scroll. But since the scrollbar really breaks our minimal design in browsers on Windows, we’ll use a little trick to hide it. We’ll set the main wrapper to be overflow hidden, with a certain width (initially it’s just wide enough to see the icon bar). Then we’ll give the scroll wrapper a slightly larger width and a height of 100%. The scrollbar will be hidden. Our menu will then extend to the height it needs and it will be scrollable.
Initially we want to hide the menu, so we’ll give it a negative left value (of its width). Why are we not using 2D translate here?

.gn-menu-wrapper {
    position: fixed;
    top: 60px;
    bottom: 0;
    left: 0;
    overflow: hidden;
    width: 60px; /* will be transitioned to 340px */
    border-top: 1px solid #c6d0da;
    background: white;
    transform: translateX(-60px); /* will be transitioned to 0px */
    transition: transform 0.3s, width 0.3s;
}

.gn-scroller {
    position: absolute;
    overflow-y: scroll;
    width: 370px;
    height: 100%;
}

.gn-menu {
    border-bottom: 1px solid #c6d0da;
    text-align: left;
    font-size: 18px;
}

Let’s add a box shadow for separating the list items. This will help us avoid double lines when hiding the submenu items:

.gn-menu li:not(:first-child),
.gn-menu li li {
    box-shadow: inset 0 1px #c6d0da
}

Let’s add a transition the the submenu list items and set their initial height to 0:

.gn-submenu li {
    overflow: hidden;
    height: 0;
    transition: height 0.3s;
}

The color will be slightly lighter than the parent menu items:

.gn-submenu li a {
    color: #c1c9d1
}

Now, let’s style the special search item and the search input specifically. We want to make it really subtle like on the Google Nexus page, so we’ll give it a transparent background colors and make the placeholders look like a normal menu item:

input.gn-search {
    position: relative;
    z-index: 10;
    padding-left: 60px;
    outline: none;
    border: none;
    background: transparent;
    color: #5f6f81;
    font-weight: 300;
    font-family: 'Lato', Arial, sans-serif;
    cursor: pointer;
}

/* placeholder */

.gn-search::-webkit-input-placeholder {
    color: #5f6f81
}

.gn-search:-moz-placeholder {
    color: #5f6f81
}

.gn-search::-moz-placeholder {
    color: #5f6f81
}

.gn-search:-ms-input-placeholder {
    color: #5f6f81
}

Most browsers will hide the placeholder when we click on the input which is much better for the user to understand that this is an input. Chrome does not have that behavior so we’ll use a little trick to emulate the same thing by setting the color of the placeholder to transparent once the user clicks on the input and focuses it:

.gn-search:focus::-webkit-input-placeholder,
.no-touch .gn-menu li.gn-search-item:hover .gn-search:focus::-webkit-input-placeholder {
    color: transparent
}

input.gn-search:focus {
    cursor: text
}

On hover we will change the color of the input text to white, just like we do to the other anchors (this is the text the user types):

.no-touch .gn-menu li.gn-search-item:hover input.gn-search {
    color: white
}

We will also do that for the placeholder text:

/* placeholder */

.no-touch .gn-menu li.gn-search-item:hover .gn-search::-webkit-input-placeholder {
    color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search:-moz-placeholder {
    color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search::-moz-placeholder {
    color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search:-ms-input-placeholder {
    color: white
}

The search icon anchor will be a special one because it won’t have the text visible next to it. The whole list item is a trick box. You see, by setting the icon anchor to position absolute, we will let the search input start at the very left of our list item. But remember, we gave the input a large left padding which will make the text start only after out search icon. When clicking on the search icon we will be actually clicking on the input, focusing it.

.gn-menu-main a.gn-icon-search {
    position: absolute;
    top: 0;
    left: 0;
    height: 60px;
}

Now, let’s style the ::before pseudo element for the icons. We’ll set them to inline-block and give them a width of 60 pixel. We have to reset all the font styles, because now we’ll be using our icon font that we’ve included in the beginning of the CSS:

.gn-icon::before {
    display: inline-block;
    width: 60px;
    text-align: center;
    text-transform: none;
    font-weight: normal;
    font-style: normal;
    font-variant: normal;
    font-family: 'ecoicons';
    line-height: 1;
    speak: none;
    -webkit-font-smoothing: antialiased;
}

Let’s define the content for all the icons:

.gn-icon-help::before {
    content: "\e000"
}

.gn-icon-cog::before {
    content: "\e006"
}

.gn-icon-search::before {
    content: "\e005"
}

.gn-icon-download::before {
    content: "\e007"
}

.gn-icon-photoshop::before {
    content: "\e001"
}

.gn-icon-illustrator::before {
    content: "\e002"
}

.gn-icon-archive::before {
    content: "\e00d"
}

.gn-icon-article::before {
    content: "\e003"
}

.gn-icon-pictures::before {
    content: "\e008"
}

.gn-icon-videos::before {
    content: "\e009"
}

Normally, we want the text of the anchor to show next to the icon but sometimes, we only want to show the icon. But we don’t just want an empty anchor, the text should still be in the HTML. So we will wrap those special cases into a span which we will simply hide by setting the width and height to 0 and the overflow to hidden. Why not simply using display: none? Hiding the content like that would make it inaccessible to screen readers, so let’s make sure that we don’t “erase” anything so important for them:

.gn-icon span {
    width: 0;
    height: 0;
    display: block;
    overflow: hidden;
}

Let’s not forget about our little menu icon in the main menu. So, we won’t use an icon from the icon font here, although you of course could. Instead we’ll create it with a box shadow that will use alternating colors (background and blue) to create the three lines. You could also use a gradient here if you prefer.

.gn-icon-menu::before {
    margin-left: -15px;
    vertical-align: -2px;
    width: 30px;
    height: 3px;
    background: #5f6f81;
    box-shadow: 0 3px white, 0 -6px #5f6f81, 0 -9px white, 0 -12px #5f6f81;
    content: '';
}

On hover, we will invert the box shadow colors:

.no-touch .gn-icon-menu:hover::before,
.no-touch .gn-icon-menu.gn-selected:hover::before {
    background: white;
    box-shadow: 0 3px #5f6f81, 0 -6px white, 0 -9px #5f6f81, 0 -12px white;
}

And when it’s selected (our side menu is open), we’ll make it more blue:

.gn-icon-menu.gn-selected::before {
    background: #5993cd;
    box-shadow: 0 3px white, 0 -6px #5993cd, 0 -9px white, 0 -12px #5993cd;
}

The last thing we need to do is to define our two classes for opening the menu for showing the icons only and for showing the whole menu. When we hover over the menu icon, we will show the icons only. Let’s call this class gn-open-part. The other class, gn-open-all will be applied either if we click on the main menu icon or if we hover over the icon part shown (the menu sidebar menu itself).
In both cases, we’ll need to reset the translate to 0:

.gn-menu-wrapper.gn-open-all,
.gn-menu-wrapper.gn-open-part {
    transform: translateX(0px);
}

If we want to open the whole menu, we’ll need to set the right width:

.gn-menu-wrapper.gn-open-all {
    width: 340px;
}

Opening the whole menu, should also expand the submenu items:

.gn-menu-wrapper.gn-open-all .gn-submenu li {
    height: 60px;
}

Last, but not least, our precious media query that will make the menu use the whole width of the screen:

@media screen and (max-width: 422px) { 
    .gn-menu-wrapper.gn-open-all {
        transform: translateX(0px);
        width: 100%;
    }

    .gn-menu-wrapper.gn-open-all .gn-scroller {
        width: 130%;
    }
}

And we’ll also adjust the width of the scroll wrapper to be larger than the 100%. This is probably not too important as we don’t see scrollbars on most devices of that size.

Alright, now that we’ve styled everything, we’ll use some JavaScript for the logic of opening and closing the menu (i.e. applying the classes).

The JavaScript

So let’s create a small script that will take care of the menu functionality. When we hover over the menu icon, we want the first part of the menu to slide out so that we can see the icons. If we hover over the sidebar menu area or if we click on the main menu icon, then the rest of the menu should slide out. Clicking the menu icon again or clicking on any other part of the body should make the whole menu slide back in. So let’s see how we can pull all that off.

We start by caching some elements and initializing some variables. The bodyClickFn function defines what happens when the menu is open and we click somewhere else on the document. We should also take care of touch events.

    _init : function() {
        this.trigger = this.el.querySelector( 'a.gn-icon-menu' );
        this.menu = this.el.querySelector( 'nav.gn-menu-wrapper' );
        this.isMenuOpen = false;
        this.eventtype = mobilecheck() ? 'touchstart' : 'click';
        this._initEvents();

        var self = this;
        this.bodyClickFn = function() {
            self._closeMenu();
            this.removeEventListener( self.eventtype, self.bodyClickFn );
        };
    }

Let’s take a look at the events that need to be initialized.
We want to open the first part of the menu (let’s call it icon menu) when the main menu icon (trigger) is hovered. When we move the mouse out this same menu should slide back in.

    this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } );
    this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } );

Once the icon menu is in the viewport, hovering over it will make the rest of the menu slide out. After it slides out, and we click somewhere on the body then the menu should slide back in. We need to bind the respective event (click or touchstart) to the document.

    this.menu.addEventListener( 'mouseover', function(ev) {
        self._openMenu(); 
        document.addEventListener( self.eventtype, self.bodyClickFn ); 
    } );

Finally if we click the menu icon, we want the whole menu to slide out or slide in if it’s already in the viewport. We will also bind (or unbind) the respective event (click or touchstart) to the document.

    this.trigger.addEventListener( this.eventtype, function( ev ) {
        ev.stopPropagation();
        ev.preventDefault();
        if( self.isMenuOpen ) {
            self._closeMenu();
            document.removeEventListener( self.eventtype, self.bodyClickFn );
        }
        else {
            self._openMenu();
            document.addEventListener( self.eventtype, self.bodyClickFn );
        }
    } );

One last thing: we don’t want the menu to slide back in if we click somewhere inside the menu area. Since we are binding the click/touchstart event to the document (so that the menu closes) we need to do the following:

    this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } );

And here is the final _initEvents function and the methods to open and close the menu.

    _initEvents : function() {
        var self = this;

        if( !mobilecheck() ) {
            this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } );
            this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } );
        
            this.menu.addEventListener( 'mouseover', function(ev) {
                self._openMenu(); 
                document.addEventListener( self.eventtype, self.bodyClickFn ); 
            } );
        }
        this.trigger.addEventListener( this.eventtype, function( ev ) {
            ev.stopPropagation();
            ev.preventDefault();
            if( self.isMenuOpen ) {
                self._closeMenu();
                document.removeEventListener( self.eventtype, self.bodyClickFn );
            }
            else {
                self._openMenu();
                document.addEventListener( self.eventtype, self.bodyClickFn );
            }
        } );
        this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } );
    },
    _openIconMenu : function() {
        classie.add( this.menu, 'gn-open-part' );
    },
    _closeIconMenu : function() {
        classie.remove( this.menu, 'gn-open-part' );
    },
    _openMenu : function() {
        if( this.isMenuOpen ) return;
        classie.add( this.trigger, 'gn-selected' );
        this.isMenuOpen = true;
        classie.add( this.menu, 'gn-open-all' );
        this._closeIconMenu();
    },
    _closeMenu : function() {
        if( !this.isMenuOpen ) return;
        classie.remove( this.trigger, 'gn-selected' );
        this.isMenuOpen = false;
        classie.remove( this.menu, 'gn-open-all' );
        this._closeIconMenu();
    }

And that’s all! Thank you for reading and I hope you enjoyed this tutorial and find it useful!

View demo Download source

Previous:
Next:

Tagged with:

Mary Lou (Manoela Ilic) is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by

Website: http://tympanus.net/

Related Articles

Feedback 113

  1. 1

    Great tuto!

    I was wondering if we can execute this demo in “Local” Off line… Because when I am working “Of Line “(Hors ligne), I have not the same results!!

    Thanks

    • 2

      Codrops Rules !
      @Marquise Mery -> When you work “localy” (i.e. off-line), don’t forget to change the path that links to “gnmenu.js ” and/or “classie.js”
      I did the same mistake at first
      :-)
      En français dans le texte : en local, on oublie régulièrement de changer l’adresse du chemin qui pointe vers un ou plusieurs fichiers nécessaire au bon fonctionnement du tuto. Généralement c’est soit le fichier .css ou le(s) .js
      Par exemple au lieu de “http://www.mon-site.com/dossier/sous-dossier/gnmenu.js” ; tu devrais avoir -> http://localhostxxx/dossier/sous-dossier/gnmenu.js

  2. 3

    In the native Android browser, there is an error with the TranslateX (-60px) property;
    remains open “gn-menu-wrapper.”;

    To fix it you must do the following.
    TranslateX (-60px) for translate3d (-60px, 0px, 0px)


    .gn-menu-wrapper {
    z-index: 10;
    position: fixed;
    top: 40px;
    bottom: 0;
    left: 0;
    overflow: hidden;
    width: 60px;
    border-top: 1px solid #c6d0da;
    background: white;
    -webkit-transform: translate3d(-60px, 0px, 0px);
    -moz-transform: translate3d(-60px, 0px, 0px);
    transform: translate3d(-60px, 0px, 0px);

    /*** Changue translateX(-60px) for translate3d(-60px, 0px, 0px) */

    /* -webkit-transform: translateX(-60px);
    -moz-transform: translateX(-60px);
    transform: translateX(-60px); */
    -webkit-transition: -webkit-transform 0.3s, width 0.3s;
    -moz-transition: -moz-transform 0.3s, width 0.3s;
    transition: transform 0.3s, width 0.3s;
    }

  3. 4

    Awesome, NAV!!!

    I found a bug
    It seems whenever you focus on a select element it hovers the icon menu. Anyone know why it would do that?

  4. 5

    I have a problem. Whenever is use this menu i get an error Uncaught ReferenceError: gnMenu is not defined. What can i do to fix this error. Any help will be helpfull

  5. 8

    Stop Propagation event is affecting other jquery plugins i have used inside that menu ???? what should i do ?? can any one help me plz ?

    No plugin is working inside that menu ..

  6. 9

    Does anyone have a fix for Windows Mobile? There is an issue with the menu not closing properly and I’m wondering why? I’ve tested the menu on everything else and it works perfectly! Does anyone know what’s causing the issue with Windows mob so I can look for a fix?

    Thanks,
    Mark.

  7. 10

    A great piece of work right there, as always!
    But it just wont work for me… Idk why.. I copied every line 1:1 from this tutorial, but it doesn’t work.
    I checked the files on github and compared… it only works for me together with that “classie.js”. But the fact that nothing out of that classie.js is listed in this tutorial leaves me thinking that that file/script isn’t really necessary.
    I’m despairing! :(

  8. 11

    I’m trying to add this menu to the top of wordpress outside the canvas so I can navigate between multiple wordpress sites. I renamed the index.html to nav.html and added it to the twebtyfourteen header.php:

    just before the body tag:

    <body >

    The menu displays fine but the slide menu on the side does not work. I’m developing on a local environment

  9. 12

    Did anyone figure out how to have submenus hidden until the user clicks on that specific menu item? If so, please let me know.

Follow this discussion

Leave a Comment

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>