Hybrid CSS Dropdowns

I know what you’re thinking…“Do we really need another article about CSS dropdowns?” Allow me to convince you. What if we could have one clean, well-structured menu which would combine the dynamism and code-ease of dropdown menus and do away with their main problems (not to mention degrade beautifully)? The problems with dropdown menus are:

Article Continues Below
  1. their secondary options are inaccesible unless you activate the entire menu system; and
  2. they offer insufficient orientation cues for the user. It can be difficult to navigate within a particular section of the site because you have to go back to the dropdown to change pages.

This technique is a bulletproof way to ensure browser compatibility and to maintain usability even for people who have old browsers or difficulty accessing dropdown menus, either because of a disability or a low level of comfort with the dropdown paradigm. It also does a much better job than standard dropdown menus of orienting the user within the site.

Warning: This technique will not work as well for lists that require large numbers of items, where dropdowns either shine or collapse under the weight of their own sheer mass, depending on your perspective.

We’re going to create a hybrid menu that runs horizontally across the window. It has two levels of navigation (in our example, main topics and their associated pages). Our menu will allow for dropdown access to all pages in both navigation levels, display the current choices in the selected topic area constantly, keep the user aware of where he is in the site, and be clean and light to boot.

Sound good? Let’s get going.

First, we need a list#section2

Let’s start with a list of architectural periods and some of their major representatives. We will attach an ID to the

    element and classes of “off” and “on” to the main list items (which is probably not the best solution, but will work for this article’s purposes):

    Get some style on#section3

    This is a great place to start. Simple, semantic markup that can be maintained easily and in one place. It looks like you would expect it to look.

    The first thing we are going to do with our CSS is to display the primary level horizontally (using float) and hide all of the subnavigation lists. We will also set the display for the links in the list to be bold, colored, and have a border.

    #nav li {
      /*float the main list items*/
      margin: 0;
      float: left;
      display: block;
      padding-right: 15px;
    }#nav li.off ul, #nav li.on ul {
      /*hide the subnavs*/
      display: none;
    }#nav li a {
      /*for all links in the list*/
      color: #f90;
      font-weight: bold;
      display: block;
      height: 15px;
      width: 100px;
      border: 1px solid #29497b;
      padding: 5px;
    }
    

    Next, let’s position our second nav level below the main list, so that when it does show up again, it’s in the right place.

    #nav li.off ul, #nav li.on ul {
      /*put the subnavs below and hide them all*/
      display: none;
      position: absolute;
      top: 33px;
      height: 15px;
      left: 0;
      padding-top: 10px;
    }
      
    

    Finally, we’ll show the subnavigation bar for the active topic area, “Modern.” The best way to do this if you want only one central, complete menu file, is with a body class of, say, “Modern,” and corresponding selectors. For this article, which will be published in someone else’s body element and should remain self-sufficient, we have set a class of “on” to the active topic and “off” to the inactive topics.

    #nav li.on a {
      /*change border color for active topic area*/
      border: 1px solid #f90;
    }#nav li.on ul a, #nav li.off ul a {
      /*  cancel inherit of border
          on subnav of active topic */
      border: 0;
    }#nav li.on ul {
      /*display active subnav list*/
      display: block;
    }
    

    After adding a couple borders, you can see what we have so far here.

    So they all rolled over and one fell out…#section4

    Now, we activate the rollover. This is not much different than any other CSS dropdown menu—the hover is on the li element, so IE will choke due to its poor implementation of the :hover psuedo-class. We’ll get to that in a minute. The following CSS removes the border on the second nav level, sets the active subnav to always display, and sets the inactive subnavs to display when their parents are hovered. We’ll set a z-index to be sure that the hovers always take precedence over the current topic area’s subnav.

    #nav li.on ul a, #nav li.off ul a {
      float: left;
      /*ie doesn't inherit the float*/
      border: 0;
      color: #f90;
      width: auto;
      margin-right: 15px;
    }#nav li.on ul {
      /*display the current topic*/
      display: block;
    }#nav li.off:hover ul {
      /*  display the other topics when
          their parent is hovered */
      display: block;
      z-index: 6000;
    }
    

    We’ll make it a little more user-friendly, with a background on the hovered tabs.

    #nav li.off a:hover, #nav li.off:hover a {
      background: #29497b;
      color: #f90;
    }  
    

    Check in on our progress here.

    Accommodating our “special” browser friends#section5

    You have two options for users of browsers such as, say, Internet Explorer, which lack support for :hover on anything but the <a> element. You can either leave the menu as is, knowing that it will work beautifully in a few years when all browsers support :hover (knock on wood), and resting in the knowledge that all the navigation options will be visible and accessible to all users, or you can work up a bit of JavaScript to rewrite the CSS selectors in language IE understands to make the dropdown dynamism accessible to all.

    (Working in IE as-is being relative.) We do have to adjust the positioning a little using the asterisk hack:

    #nav li.off ul, #nav li.on ul {
      /*put the subnav below*/
      top: 33px;
      *top: 44px; /*reposition for IE*/
    }

    So it’s working beautifully in modern, standards-compliant browsers, and working in a functional, degraded way, with correct positioning, in versions 5 and 6 of Internet Explorer. With a little help, we can make the hovers work in IE. This simple JavaScript rewrites the hovers as mouseover events, and works for all versions of IE/Win 5x and 6x. Much thanks to Patrick Griffiths and Dan Webb, whose “Suckerfish Dropdowns” got me started with CSS-based menu systems. Their snippet of JavaScript looks like this:

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

    With a simple change to the CSS:

    #nav li.off:hover ul, #nav li.over ul { 
      display: block;
      z-index: 6000;
    }#nav li.off a:hover,
    #nav li:hover a,
    #nav li.over a {
      background: #29497b;
      color: #f90;
    }

    to add the class “.over” to the list items that require hovering, the list functions just as well for you IE/Win users. Not that you shouldn’t think about a new browser, but for now we’ll continue to support the masses in the manner to which they’ve become accustomed.

    So that’s it. An incremental change, perhaps, over the previous work with CSS dropdowns, but another angle for you to explore for those instances where it really is useful to have the navigation options displayed rather than hidden inside a dropdown menu.

    But can it be beautiful?#section6

    Ok, that’s not it. I couldn’t leave you without a slightly more graphically-rich version to take this technique out of the pedantic and into the real world. With a few changes, a CSS sprite navigation image (thanks, Dave Shea), a photo I took in NYC, and a bit more CSS, we get a menu system which really shows the power of CSS combined with graphic design. Check out the final Hybrid CSS Dropdown, fully functional in all modern browsers.

About the Author

Eric Shepherd

Eric Shepherd is the founder of arkitrave web media, a small web and graphic firm in Buffalo, New York. He is finishing a Master of Architecture degree at the University at Buffalo. He is also a freelance pianist. His work has been featured in the venerable CSS Zen Garden, and he is a strategic partner of Nepo Strategies, a Buffalo-based e-commerce and web strategy firm. He's already working on explaining the potential of clean XHTML, CSS, and Javascript combined with the DOM to his brilliant one-month-old daughter Naia.

110 Reader Comments

  1. Your menu behaves badly in Firefox when I increase text size (Ctrl++). Menu items disappear, extraneous lines appear, etc..

  2. The problem with CSS menus is that navigation is very important and problems with like the flashing effect, cross-browser issues, etc. make it less than ideal.

    Another problem I have with CSS only menus is that it tries to do behaviour in CSS for which CSS was not designed. CSS should IMO be presentation only.

    Also with XHTML markup, CSS styled but Javascript powered navigation you can do some eye candy like the menus I did for my MXR CMS product. see http://mxdemo.infireal.com/ (P.S: the markup is terrible, its on the todo list)
    The menu does a roll-up and fade out effect.

  3. I’m having a problem implementing a list in FireFox. When I added the style info into my stylesheet, all of a sudden FireFox stopped recognizing all of the stylesheet from that point down. I can’t figure out what’s causing this problem. IE can still parse the entire stylesheet, displaying the list properly. (The list is taken from the ALA 2002 article Taming Lists by the way.)

    Any suggestions? Thanks!

  4. I was just checking out the linked examples and they look fine in Safari, but totally not working in Explorer??

  5. Sorry for a little off-topic, but I’m struggling with one thing, which maybe you know how to solve

    I want a very simple horizontal menu, based on list

    I’ve done it, but I wanted it to have a custom bullet, so I used list-style: url(/image.gif); in the ul style declaration

    it appeared ok in mozilla, but there is NO image neither in opera nor IE.

    well, if I change the image to just ‘circle’ or ‘square’ it does not appear in those browsers anyway

    Is it possible to to have bullets (custom too) in horizontal menu based on ul ?
    it’s easy to make it in vertical on, but I have not found any example of such menu turned to horizontal…

    thanks

  6. I am in the process of setting this up and I can’t get it to center on the page like the rest of my page does. I have a container div around my whole page with margin-left and margin-right set to auto, but the secondary nav is always left aligned. Anyone know a remedy?

  7. I was working several mothes ago on a similar NAV on a client site http://www.cfatb.com/ using extensively the sluckerfish inspiration.

    I enhanced it to fit our requisites and it seems that it works good on many major browsers. Of course the CSS is free to use as well as the HTML code itself. I think I made some mistakes but it works pretty well.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career