Using CSS to Generate Expanding Horizontal Navigation Menus in Drupal

Not to toot my own horn, but everyone loves this site's top navigation menu. I've received more questions about how I did it than I can answer. As a result, I've decided to write a tutorial on it. Before beginning this tutorial, the reader should be somewhat familiar the following:

Also, be sure that you have installed PHP-Template, and a fresh copy of Box_Grey on hand.

Now a quick word of warning: This tutorial, and these concepts are not for the weak-willed and weak-minded. The concepts we're going to cover are inherently frustrating, difficult, and confusing. However, if you work to understand them, and make a commitment to get this right, I promise huge rewards in your overall understanding of CSS. And take it from me, a solid understanding of CSS means more than personal satisfaction, it can also make you a reasonable living.

Phase I: Prepare for Battle

First things first, unzip yourself a new install of box_grey, and rename the "box_grey" folder "madness". Now, let's include a seperate stylesheet for our navigation menu. Using a text editor, open up the file themes/madness/page.tpl.php. On line 8 add some code for the new stylesheet like a so:

@import "themes/madness/menu.css";

Now, we are going to replace what used to be the primary and secondary links with a dynamic full user menu. Go to line 31 and delete everything within the "top-nav" DIV, and replace it with the following code:

<?php $UserMenu = theme_menu_tree(0); print $UserMenu; ?>

By now, you should save your work and check out your site. You should see this:

css

Phase II: Inlines and Floats: Your CSS Sledge Hammer

Obviously, we're not done... Our menus are displaying vertically, and have those lovely box and arrow bullets. My friends, we have no choice but to bring in the CSS.

Remember in the earlier step when I told you to include the file themes/madness/menu.css? Well, we sort of forgot to create that file, so let's go ahead and do it. Hopefully, you don't need me to explain how to create a file called "menu.css" in your themes/madness directory....

Open your new file "menu.css"; it should be a completely blank file. Take a deep breath reader, your understanding of CSS is about change dramatically. Now might also be a good time to brew a pot of coffee. Now its time to go medioeval on this nested list's ass. Write your first rule:

#top-nav li {display:inline;}

So before, our menu was standing up all tall talking trash about our mothers; and we just couldn't let that stand. So we said, "Okay, stupid nested menu, say hello to my little friend!" Or, the more technical version is we told our CSS document that all lines within the top-nav div to display inline. Here is the result:

we totally kicked this menu's ass

Mmmhmmm, the above menu just got served. However, as we will soon find out, our menu still thinks it can talk trash. Click a link to another layer of menus (i.e. click administer). Look at the nerve:

navigation menu still talking trash

At first glance this looks like a nightmare. The weak will give up at this point and simply declare "it cannot be done!". However, we are strong, and we're going to crush this minor inconvience.

The reason that the new layer of menus is splitting its parent menu in half can be found at the markup level. Drupal generates new menus within menu lines like a so:

  • EXPANDED MENU ITEM
    • MENU ITEM

Now that we understand how this markup works, we can bust out some CSS-kung fu. Now Grasshopper, add two more CSS rules

#top-nav li {display:inline;} #top-nav ul li ul {float:left; width:100%;}

Pay close attention to how this new CSS rule is written. Where as our first rule #top-nav li applies to all lines within the top-nav div, our new rule is much more specific. In English, it says we only apply this rule only within the #top-nav id, and to unordered lists within lines of an unorder list. Sound confusing? It is, but only because I tried to put it into english. Take a look progression in HTML code for our nested lists a few lines up, follow the elements to "MENU ITEM"; they are:(1)#topnav, (2)ul, (3)li, (4)ul. Look familiar? Perhaps now you'll begin to understand why many geeks wish we spoke code instead of english. In all cases, following the progression like this helps you apply CSS rules like a sharp shooter.

We've told #topnav ul li ul to "float:left". This slides it to the left (obviously), but more importantly, causes the new layer of lists to no longer split parent menus in half, or leave empty space. However, float also by default will causes our list to shrink to its minimum required width. So we've overrided that by specifying "width:100%". Now, look at the magnificent result:

menu beginning to behave

SUPRISE! There is another problem, and this one is particularly scary looking. While the second layer of lists behaves perfectly, the third layer corrupts our entire layout, sliding to the right by no less than 1000px!

what happened

Frankly reader, we don't know, and we don't care. There is any easy solution. Add your third CSS rule and finish today's foe for good:

#top-nav li {display:inline;} #top-nav ul li ul {float:left;width:100%;} .clearnav {clear:both;}

Shazam, one shot, one kill. ".clearnav {clear:both}" was the missing link. We now have menus which will expand horizontally, without problems, into infinity. If you must know "clear" typically clears out any odd behaviors that are caused by floats. Observe our unstyled menu's beauty:

quarter of the way there

The good news is that by this point we're about a quarter of the way there. Er, wait that's the bad news, sorry. Today, we've merely created the foundation to our menus. However, I want to personally congradulate all three of you that have managed to get this far. In our next tutorial I will teach you how to style the individual levels of the menus. Specifically, we'll cover how to apply different styles for menus that are expanded, collapsed, active, or dead ends. In our third tutorial we will cover advanced styling such as shades, rounded tabs, or entirely different displays (however, I myself am still figuring out how to do that...)

As always, please let me know if you have any problems with this tutorial. I will not only be happy to help you, but most likely others are experiencing the same problems as your are. So asking for help not only helps you, but it helps anyone else who is attempting to learn these concepts.