Nicer Navigation with CSS Transitions

February 3, 2010
Development

Update: March 21, 2014 - We have an updated post after years of practice with these techniques. 


Newer versions of Apple's Safari web browser (and Google Chrome, which is also based on Webkit) support a vendor-specific implementation of the CSS3 Transition property. CSS Transitions are a very powerful effect that can eliminate the use of JavaScript for many common effects.

Quick Overview

There are three properties that make up the transition: -webkit-transition-property, -webkit-transition-duration, and -webkit-transition-timing-function. Additionally, there is a shorthand property that combines the three: -webkit-transition.

-webkit-transition-property: This specifies which properties of the element will be animated. If it is set to all every available property will be animated, otherwise you can choose to animate one or more individual properties and leave the rest as they are.

-webkit-transition-duration: The amount of time from the beginning of the transition to the end. This is specified in seconds: 1s, 0.5s, etc.

-webkit-transition-timing-function: There are a number of timing functions—mathematical models for how the transition takes place—that you can choose from. These include ease, linear, ease-in, ease-out, and ease-in-out as well as the ability to create your own with cubic-bezier. A detailed explanation of these modes is beyond this article, please experiment with them or see the article on The Art of Web for more detail.

-webkit-transition combines all three into a handy shortcut: -webkit-transition: all 1s ease;

A Simple Example

a { background: #fff; color: #aa0000; padding: 3px; -webkit-transition: all 1s linear; }
a:hover { background: #220077; color: #fff; }

This is a link

When you hover over the link above you will see the text fade to white and the background fade to purple. (One of the great things about using Transitions in this way is that they degrade gracefully. If you are using a browser other than Safari you will see a normal hover effect with no transition.)

Navigation

We can use CSS Transitions to create a very nice navigation menu without JavaScript (well, maybe a little for Internet Explorer). We will start off by creating the menu using the standard CSS properties, to be sure it will degrade nicely in browsers other than Safari.

HTML

<ul id="trans-nav">
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Products</a>
        <ul>
            <li><a href="#">Widgets</a></li>
            <li><a href="#">Thingamabobs</a></li>
            <li><a href="#">Doohickies</a></li>
        </ul>
    </li>
    <li><a href="#">Contact</a></li>
</ul>

CSS:

#trans-nav { list-style-type: none; height: 40px; padding: 0; margin: 0; }
#trans-nav { list-style-type: none; height: 40px; padding: 0; margin: 0; }
#trans-nav li { float: left; position: relative; padding: 0; line-height: 40px; background: #5a8078 url(nav-bg.png) repeat-x 0 0; }
#trans-nav li:hover { background-position: 0 -40px; }
#trans-nav li a { display: block; padding: 0 15px; color: #fff; text-decoration: none; }
#trans-nav li a:hover { color: #a3f1d7; }
#trans-nav li ul { opacity: 0; position: absolute; left: 0; width: 8em; background: #63867f; list-style-type: none; padding: 0; margin: 0; }
#trans-nav li:hover ul { opacity: 1; }
#trans-nav li ul li { float: none; position: static; height: 0; line-height: 0; background: none; }
#trans-nav li:hover ul li { height: 30px; line-height: 30px; }
#trans-nav li ul li a { background: #63867f; }
#trans-nav li ul li a:hover { background: #5a8078; }

Notice we have a nice drop-down menu under "Products" and hover state on the top level menu items.

Enhance It!

With just four lines of CSS and without changing anything in other browsers, we can now make the menu a little nicer in Safari.

#trans-nav li { -webkit-transition: all 0.2s; }
#trans-nav li a { -webkit-transition: all 0.5s; }
#trans-nav li ul { -webkit-transition: all 1s; }
#trans-nav li ul li { -webkit-transition: height 0.5s; }

You might have noticed a few oddities in our original CSS. For instance, instead of hiding the submenu by settings display:none we set the height of all the sub-menu items to 0. That was in preparations for our slide-down transition. Since you can't apply a transition to display we need to find a workaround. We are also animating the hover color on the links and the background image position to get the nice fade between the normal and "pressed" states.

If you want to learn more about the details of CSS transition, see the W3's specification. There is a lot of room for creativity with this, so please share any unique transitions you come up with!

UPDATE!

Through a combination of extra css styles and JavaScript, you can make the menus work well in Internet Explorer, too! First, for all versions, you will need to hide and show the sub-menus using display: none rather than by setting the height of the menu items to zero, which leaves an ugly block of the menu's background color.

#trans-nav li ul { display: none; }
#trans-nav li:hover ul, #trans-nav li.over ul {display: block; }
#trans-nav li ul li { height: 30px; line-height: 30px; }

(Obviously, this should be in a separate stylesheet that is called with conditional comments, so it only affects IE.)

That will fix things for IE 7 and 8. Since IE 6 doesn't support the :hover pseudo-element, we need a bit of JavaScript (shamelessly stolen from the Suckerfish Dropdowns method) to fake it.

startList = function() {
    if (document.all && document.getElementById) {
        var navRoot = document.getElementById("trans-nav");
        for (i=0; i<navRoot.childNodes.length; i++) {
            var node = navRoot.childNodes[i];
            if (node.nodeName=="LI") {
                node.onmouseover=function() {
                    this.className+=" over";
                }
                node.onmouseout=function() {
                    this.className=this.className.replace(" over", "");
                }
            }
        }
    }
}
window.onload=startList;

And there you have it! The above demos don't work, in IE, since TinyMCE keeps messing with my code, but you can see the final version.

Comments

Jagjit's avatar
Jagjit
WOW. Interesting.
Sohel rana's avatar
Sohel rana
Nice tutorial!
Stephanie's avatar
Stephanie
I've got a two-tiered menu that I'm trying to convert from Java Script to CSS. It's working fairly well, but I can't get the sub-menus to hide until I have over their container in the main menu. For example, under Presbytery, the menu listed under Staff (Executive Presbyter, Stated Clerk) should not appear until I hover over Staff. The stylesheet for the menu is at http://www.presbyteryov.org/menupov/menupov.css. I tried setting the opacity to 0, then setting it to 1 when it's hovered over like the main menu, but it's not working.
Lillie's avatar
Lillie
This worked really well for me. I'm currently redesigning a Quilt Guild web site. It's in test mode right now so don't really have a url I can give you. Thanks so much for the code.
Name *'s avatar
Name *
they are equivalent propeties for firefox:
-moz-transition: all 3s ease;
and for ready navigator (firefox and chrome support final version):
transition: all 3s ease;

Leave a comment