Nicer Navigation with CSS Transitions

Taking our second look at the topic after years of practice

March 21, 2014
Development, Front End

It's been four years since we wrote our first post about improving your site navigation with some simple CSS animation, so we thought it would be a good time to revisit the subject. Here we'll describe a few additional animations that you can use. If you want to jump ahead and see the final products, you can view the demo.

Markup

All of the examples use the same markup consisting of a simple unordered list.

<nav class="nav">
    <ul>
        <li>
            <a href="#">Nav Item</a>
            <ul>
                <li><a href="#">Subnav Item</a></li>
                <li><a href="#">Subnav Item</a></li>
                <li><a href="#">Subnav Item</a></li>
            </ul>
        </li>
        <li>
            <a href="#">Nav Item</a>
            <ul>
                <li><a href="#">Subnav Item</a></li>
                <li><a href="#">Subnav Item</a></li>
                <li><a href="#">Subnav Item</a></li>
            </ul>
        </li>
    </ul>
</nav>

Basic CSS

.nav ul {
    *zoom:1;
    list-style:none;
    margin:0;
    padding:0;
    background:#333;
}
.nav ul:before,.nav ul:after {
    content:"";
    display:table;
}
.nav ul:after {
    clear:both;
}
.nav ul > li {
    float:left;
    position:relative;
}
.nav a {
    display:block;
    padding:10px 20px;
    line-height:1.2em;
    color:#fff;
    border-left:1px solid #595959;
}
.nav a:hover {
    text-decoration:none;
    background:#595959;
}
.nav li ul {
    background:#273754;
}
.nav li ul li {
    width:200px;
}
.nav li ul a {
    border:none;
}
.nav li ul a:hover {
    background:rgba(0,0,0,0.2);
}

This CSS puts the top-level nav items in a line with some basic styling, and sets the groundwork for the various drop-down methods that follow. The most important thing here is the position: relative on .nav ul > li. Without this the dropdown menus would not be positioned "relative" to the parent item.

Not Animated

.nav li ul {
    position:absolute;
    left:-9999em;
    top:36px;
    z-index:1;
}
.nav ul > li:hover ul {
    left:0;
}

I thought I should start off with a very basic, non-animated drop down to set the stage. The idea is to absolutely position each drop down, relative to it's parent li, to the left -9999em. This number is probably a bit of overkill, but it more-or-less guarantees that it will be off-screen. Because it is positioned to the left there is no chance of it creating scroll bars as it may do if positioned to the right. Once the user hovers over the li, the child ul is repositioned to the left: 0, and it comes back into view. It's worth noting that there are multiple methods to implement a non-animated drop down that appears on hover, this is just one of the more common ways to do it.

Fade In/Out

.nav li ul {
    position:absolute;
    left:0;
    top:36px;
    z-index:1;
    visibility:hidden;
    opacity:0;
    filter:alpha(opacity=0);
    -webkit-transition:200ms ease;
    -moz-transition:200ms ease;
    -o-transition:200ms ease;
    transition:200ms ease;
}
.nav ul > li:hover ul {
    visibility:visible;
    opacity:1;
    filter:alpha(opacity=100);
}

To fade the menu in and out we employ both visibility and opacity style attributes. Unlike the previous example, we start off with the drop down positioned where it will be when visible. To hide it we use both visibility and opacity. The reason we use both is because both carry a characteristic that the other doesn't have. opacity can be transitioned, meaning we can transition from opacity: 0 to opacity: 1 and accomplish the fade effect. The problem is that opacity: 0 doesn't remove the element. The links would still be clickable even though they are not visible. visibility doesn't remove the element either, but it does remove any functionality available to the user, so it's like it's not even there.

Slide Down

Creating a menu where the drop downs appear to slide or expand down from the bottom of the parent element is simple, but you have to follow some rules. My first attempt at it was to set the height of the entire drop down ul to height: 0 then transition to height: auto. I quickly found out that this doesn't work as transitions will not work with height: auto. This means that you either need to have a fixed height for all of your drop downs or you have to think of something else. Yes, I know you can use javascript to find the height of each element and then transition to that height but my goal is to not use any javascript. Here are a couple of options.

Slide Down #1

.nav li ul {
    position:absolute;
    left:0;
    top:36px;
    z-index:1;
}
.nav li ul li {
    overflow:hidden;
    height:0;
    -webkit-transition:height 200ms ease-in;
    -moz-transition:height 200ms ease-in;
    -o-transition:height 200ms ease-in;
    transition:height 200ms ease-in;
}
.nav ul > li:hover ul li {
    height:36px;
}

This approach sets the height of each list item in the drop down to height: 0 and overflow: hidden, and then transitions to a set height for list items. The overflow: hidden prevents the contents of the item to be visible when the height is set to zero. This works beautifully until you have a page title that is too long to fit on one line.

Slide Down #2

.nav li ul {
    position:absolute;
    left:0;
    top:36px;
    z-index:1;
}
.nav li ul li {
    overflow:hidden;
    max-height:0;
    -webkit-transition:max-height 500ms ease;
    -moz-transition:max-height 500ms ease;
    -o-transition:max-height 500ms ease;
    transition:max-height 500ms ease;
}
.nav ul > li:hover ul li {
    max-height:150px;
}

This approach uses max-height instead of height. Just like height, you can transition to a set max-height value. Because an element with a max-height can be any height up to and including that height, all you have to do is set a max-height that is higher than all of your drop downs will ever be and transition to it. This will result in inconsistent timings for drop downs of different height to appear because the animation is from 0 to the set max-height, not from 0 to the height of the drop down. So if max-height: 500px, and the drop down menu is 300 pixels tall, the timing of the transition will still be from 0 to 500.

Fold Out

.nav li ul {
    position:absolute;
    left:0;
    top:36px;
    z-index:1;
    max-height:0;
    overflow:hidden;
    -webkit-transform:perspective(400) rotate3d(1,0,0,-90deg);
    -webkit-transform-origin:50% 0;
    -webkit-transition:350ms;
    -moz-transition:350ms;
    -o-transition:350ms;
    transition:350ms;
}
.nav ul > li:hover ul {
    max-height:1000px;
    -webkit-transform:perspective(400) rotate3d(0,0,0,0);
}

This approach uses a combination of transitioning max-height like in the second slide down example, but adds perspective and rotation to the transition. perspective gives us depth, where the lower the number is the "closer" the object appears which gives it a much more pronounced depth effect. Higher numbers would make the effect more subtle. rotate3d with a value of -90deg creates an effect where the element appears to have rotated away from you. Adding a transition to all of these results in the desired effect. Unfortunately this implementation only works with webkit, so only Chrome and Safari users get to see it.

View The Demo

Comments

Ramana's avatar
Ramana
Its simply superb fantastic code :) How to do menu with sub menu in vertically?
NavItem
SubNavItem-> SubSubNavItem
Shashank's avatar
Shashank
Hello TODD,

Thanks for this useful tutorial.I have just used it for my project. It is looking awesome on the desktop website. Now I have made a slide nav with slideout js. The only problem is that how can i make it style in a accordion way so that when someone touches it the sub menu comes out. I'll be grateful to you if you can help me out with this as well.
shakir's avatar
shakir
Thank you so much...this is very helpful to me....
arpit's avatar
arpit
Cool Menu i found another animated menu which is very good and easy to code just by using CSS and HTML On TalkersCode.com here is that tutorial http://talkerscode.com/webtricks/animated-navigation-menu-using-pure-css-and-html.php
dilip kumar's avatar
dilip kumar
thank you so much....
Lars's avatar
Lars
Ah, my bad: I overlooked the info about the webkit support :)
Lars's avatar
Lars
Hey, I was wondering how the fold out option looks like but on my mac with firefox those demo menus look all the same:
Slide Down #1 - Slide Down #2 - Fold Out

With Safari I can see the folding out. But Slide Down are both the same.
girish jadhav's avatar
girish jadhav
thank you so much for your tutorial..!!
shagun nikam's avatar
shagun nikam
it works thkyu so much..!!!!
Allen Pagent's avatar
Allen Pagent
Great tut. Thanks!
Brad's avatar
Brad
Great tutorial: I learned a lot, but not enough. In my test page sometimes the li is horizontal instead of vertical. Second question, I've set up my test to resize as screen resolution changes. Is there an easy way to have the top ul compress into a button for mobile?

Thanks again
John's avatar
John
I'm new to CSS and I was wondering how I could expand this to a sub-sub menu.
Albert's avatar
Albert
Another cool animated navigation menu on TalkersCode.com
http://talkerscode.com/webtricks/animated-navigation-menu-using-pure-css-and-html.php
Josiah's avatar
Josiah
Hey Todd,

This is a great tutorial! I am curious if you had any tips on how to cause the same effects with the fold out example, but with the navigation floated to the right instead of left? I am trying to float it to the right and keep the nav bar on the top of the site. Any help would be appreciated!
Eric's avatar
Eric NMC team member
The tutorial is amazing,Todd.
But,my problem is I have a long dropdown menu.I wanted to add a scroll bar for it.Can reply me asap?
Thanks,
Eric Watson
Roland's avatar
Roland
Awesome, many thanks Todd!
Learned so much from your crystal clear explanation I managed to implement it in my companies CMS in about an hour.

Regards,
Roland
Chris's avatar
Chris
Wow! Never mind that one. Took only a few minutes to work it out myself. If you want to have the menus be a drop-up instead of a drop-down you just need to make the following changes:

1. Remove the top attribute from the .nav li ul section
2. Add bottom:100%; at the bottom of the .nav li ul section

This results in the menu now dropping up!
Chris's avatar
Chris
This is a great way of doing sliding menus and I thank you. One question, is there an easy way to make the menu slide up instead of down? Obviously you can change the offset in the CSS to a -xxx px value but that would only work if all menus have the same number of options. I just started playing with it so may well find a way but just wondered if you have any ideas?!

Leave a comment