A few days ago, I had the opportunity to code up the navigation of a web site that included a secondary, drop-down menu system and was asked to have it completed in the next couple days.
“Pffft, no problem”, I thought. I’ll knock it out before lunch, and move on to the next thing. I’ve built hundreds of Nav/sub-nav systems before – typically relying on Javascript to create the drop-down event. Then I discovered that this particular nav system was destined to go into a Wordpress 3 CMS based web site for use as Wordpress 3’s new, built-in dynamic menu, and that I had just two IDs or Classes that I could use in order to work with the existing Wordpress Style sheet. Then came the pièce de résistance, this was to be a pure CSS drop-down. No Javascript allowed. Oh-oh!
If you are anything like me, you probably know some of the basics of creating a purely CSS driven drop-down sub-menu; it uses the CSS pseudo-class “hover” to make the drop-down menu appear, but at least in my case, the exact details have been elusive. So after what turned out to be a six hour learning process, I now have discovered a working method for achieving what was once the Holy Grail of web site navigation. Let me see if I can share what I’ve learned.
Okay, so the plan is to build a horizontal main navigation row with a single sub-menu that drops down from the parent and will contain our secondary navigation links. Viewed from a search engine optimized, semantically correct point of view the series of links making up the main navigation of a web page is really a list of items which happen to be anchor tags. So our first step is to build an “unordered list”, or in HTML, more commonly viewed as the “UL” tag. Here’s our simple Nav row of three items.
<ul id="nav">
<li><a href="#">Link One</a></li>
<li><a href="#">Link Two</a></li>
<li><a href="#">Link Three</a></li>
</ul>
Our sub-menus will be two more unordered lists positioned as children of the second and third list-items in “nav”. I’ll drop them in there now.
<ul id="nav"> <li><a href="#">Link One</a></li> <li><a href="#">Link Two</a> <ul> <li><a href="#">Sub One</a></li> <li><a href="#">Sub Two</a></li> </ul> </li> <li><a href="#">Link Three</a> <ul> <li><a href="#">Sub One</a></li> <li><a href="#">Sub Two</a></li> </ul> </li> </ul>
Working with the two identifiers available to us from the Wordpress stylesheet, I’ve given our main navigation an ID of “nav”. I’ve chosen to use an ID in this case rather than a Class because our main navigation will be the only thing like it on the page. An ID is a unique identifier and should be used when there is only one of a certain item on a page. An ID also has greater specificity than a Class, meaning its styles will take precedence over the styles of items given a Class within the same page. Our drop-downs will be child elements of list-items within “nav” and there will be more than one, so I’m giving them both a Class called “sub-menu”.
If we were to view what we have so far in a web page we’d just have a typical bullet list like so:
- Link One
- Link Two
- Sub One
- Sub Two
- Link Three
- Sub One
- Sub Two
But we want this to be a horizontal navigation with boxes for links, and the sub-links hidden from view until we mouse-over them. Guess we’re going to have to build a Cascading Style Sheet.
You probably just said, “Well, Duh! How else would we do this?” Obvious next step, yes, but the reason I mention that we need a CASCADING style sheet is just to point out that we are working with just two identifiers in order to make all our styles for this menu system; the ID #nav, and the Class .sub-menu. Due to the specificity of #nav, its styles will directly cascade down to .sub-menu, which in some ways we want to happen, and in others, we definitely do not. In order for us to make the links in our drop-down menu have a different look and characteristics from the main navigation, my choice (if I had my way) would be to use an ID for top-level <ul> tag and a class for the top-level <li> tags, then have a another ID for the secondary <ul> tags and another class for the secondary <li> tags – this would keep everything separated and make life much easier, but given our restraints we will have to rely on listing our elements in the style sheet by descendants in order to control style inheritance.
Alright, let’s get that style sheet built starting with the top-level <ul>. We want the links to be block-level elements rather than inline elements, and we want them to float next to each other rather than be stacked as they are in the bulleted list above. While we’re at it, let’s unload those bullets too.
ul#nav { position: relative; width: 384px; height: 30px; padding: 0 8px; border-top: 1px solid #000000; border-bottom: 1px solid #000000; }
Let’s look at what I’ve done so far. First off, a UL element is a block-level object – it has height and width values and can have margins. In contrast, an inline object takes up no more space on a page then the length of the string of text and the line-height of the font used. So in our case, #nav is the container for the whole navigation system. I give it a relative position on the page which will become necessary later in order to contain our .sub-nav object. For my desired appearance, I’ve set the width value to 384px and height to 30px. I want 1px solid black borders on the top and bottom, and 8px padding on the right and left, but none on the top and bottom.
ul#nav li { display: block; float: left; height: 30px; width: 128px; overflow: hidden; list-style-type: none; background-color: #efefff; }
Now our list-items are children of the parent object, #nav. Contrary to the default setting for Li objects, we need these to be block-level elements because they will be what triggers our .sub-nav menu when the user hovers their mouse over a link contained within the list-item. We want the navigation links to line-up horizontally across the page so I float them to the left. I want the <li> to be the full 30px height of the container, and I’m giving them a width of 128px. The Overflow property is initially set to “hidden” because we do not want the .sub-nav objects to be visible until the user hovers their mouse over the target area. “List-style-type: none;” makes the bullets go away, and lastly, I’ve set a background-color on my <li> just temporarily so that I can verify that it’s behaving the way I want. I’ll remove the background-color later since it’s not really part of my final design.
ul#nav li a { display: block; width: 126px; height: 20px; margin-top: 5px; font: normal 11px/20px Verdana, Geneva, sans-serif; text-align: center; text-decoration: none; color: #000000; border-left: 1px solid #000000; background-color: #ffffaa; } ul#nav li:first-child a { border: none; }
Our next step is to style the top level anchor tags which are the actual links in our navigation. Here again, we want the anchor tags to be block level elements so that they produce a box which will provide more clicking area for the user than an inline element will. An interesting thing to note here is that in my font style I’ve set a line-height of 20px – that’s the same value as the height of my anchor tag. This will result in my text being centered vertically in the link object. The last part of this I’ll mention is that I have set a 1px solid border-left to all the anchor elements to act as a visual divider between each of the links in the navigation. However, I don’t want a line before the first link which is what we would have with this style rule, so by using the “first-child” pseudo-class I can target just the first anchor element which is a child of the list-items within #nav and set its border value to “none”. Now this is what displays on our web page:
So far, so good. We have our three top level links with text centered vertically and horizontally in their boxes. Again, the background color is just a visual aid at this time. The gray area is our block-level <li> elements, and the yellow shows us the block-level <a> elements. The second level links are successfully hidden on page load as planned, so our next step is to style them and make them appear on command.
Boom! Here it is all at once:
ul#nav li:hover {
overflow: visible;
}
ul#nav ul.sub-menu {
margin-top: 5px;
padding: 30px 0;
background-color: #dfdfdf;
border-top: 1px solid #000000;
overflow: hidden;
}
ul#nav ul.sub-menu li {
height: 15px;
font: normal 11px/15px Verdana, Geneva, sans-serif;
background-color: transparent;
}
ul#nav ul.sub-menu li a {
display: block;
padding-left: 15px;
width: 111px;
height: 15px;
margin-top: 0;
font: normal 11px/15px Verdana, Geneva, sans-serif;
text-align: left;
text-decoration: none;
color: #000000;
background-color: transparent;
border: none;
}
ul#nav ul.sub-menu li a:hover { text-decoration: underline; }
Since I’m sure you’re getting a feel for this, I’m just going to hit the high points now. The first thing to see here is the last style rule we’ll set for #nav — it’s where all the magic happens. The hover pseudo-class for <li>elements in #nav is set to “overflow: visible”, so when the user positions their mouse pointer over a list item, the overflow property switches from hidden to visible and of course the child element, which is our drop-down menu is revealed.
When setting the styles for the .sub-menu, keep in mind that it is a child of #nav and will inherit all styles that have previously been set for #nav which is the parent element. If you want the drop-down to have a different visual style than the top-level links, you will have to restate the styles with their new values in order to over-write previously set styles.
When setting independent styles for child elements, it is crucial that you indicate the descendance of the element. This specifically targets the element you are trying to style and will override earlier styles. This is why my style rules include not just the class name of the element I’m styling, but also the element from which it descends. For example “ul#nav ul.sub-menu li” targets only the <li> which are children of the class “.sub-menu”, which itself is a child of the <ul> “#nav”. If you omit the descendance and just type, “.sub-menu li” and set it’s styles, the .sub-menu list-items will inherit the style of #nav list-items because of their greater specificity and you’ll tear your hair out trying to understand why your styles don’t work. Alright. Enough said about inheritance. Let’s make sure our drop-down menu works the way we are expecting.
What we expect as a visitor to this web page is for a drop-down list of links to display when we run our mouse over the top-level link. We expect the sub-menu to vanish if we move our mouse off the main link, and we expect the sub-menu to persist if we move our mouse vertically down the sub-menu until we either move far enough south, or far enough horizontally to come off the sub-menu links. So we have to make certain there is no gap between the bottom of the main, top-level link, and the top of the first link in the sub-menu. A single pixel gap is too much. In my example, for .sub-menu, I set a margin-top of 5px which will leave one pixel remaining inside the main menu and the rest extending downward from there. Since I’m overlapping the bottom border of #nav with .sub-menu, I have added an identical 1px border at the top of .sub-menu which will visually replace the segment hidden by the drop-down. So technically, that’s about it. The rest is just styling the appearance that I want for .sub-menu by either adding new style rules or over-writing previous values to get what I want. After removing any background-color I added temporarily to check the position and size of objects, my final navigation menu looks like this:
Hopefully, I touched on enough of the sticking points in my drop-down menu build to give you the clues you’ll need to make your own build a trouble-free experience. So, good luck and happy coding.

