Current category items
Post title

Pure CSS: Drop-down Menu

Post content
Date created
Posted in "Web" at April 8th, 2008

NOTE: This Pure CSS Drop Down Menu Suite is experimental. It is not recommended that you use it on public websites. If you want a cross-browser solution, please see here. The goal of this project is to identify the issues and challenges of creating a perfect CSS Drop Down Menu for the next generation of web browsing. Specifically, this suite will not work on IE6 or earlier, and will be buggy on IE7 and other older browsers. You can alternatively skip the article and preview or download files right away.

Drop Down MenusPersonally, I’ve always admired CSS only drop down menus (hereinafter dropdown). I think it is a perfect impression of what CSS is for as well as where it is headed. Therefore, I thought that it is in my intention to show you my take on this issue and at the same time give you heads up on what else CSS can do apart from styling and give it a little of dynamics or action, if you will.

Technically, CSS2 has all necessary capabilities in order to create a nicely working dropdown menu with no help from JavaScript whatsoever. That’s basically because of the dynamic hover selector, which can be a great substitute for JavaScript functions. This is the core reason for which pure CSS dropdowns are possible in practise. Of course, there are other great CSS2 features that can also be used and contribute to this type of project. You should keep that in mind.

However, as you might know, widely used Internet Explorer browser version 6 or earlier does not support this selector. To make matters worse, IE browser 7 or earlier has major issues in terms of CSS2 support. This leads us to the problem that I can not show you the ideal CSS dropdown solution having all browsers on board. So I’ve decided to not include cross-browser support and I will simply give you a taste of what I call CSS Strict.

CSS Strict is still in my labs, but inevitably it will payoff, hopefully in the near future. I surely want to be ready. Coding in this mode is also a lot of fun, because you can use literally all the great features of CSS2 or even CSS3.

So let’s explore the main dropdown module — horizontal dropdown — and its secondary components — vertical left-to-right direction and vertical right-to-left direction dropdown — in detail.

Main Dropdown Module: dropdown.css

Okay, the first thing you should notice is that the best HTML structure for this type of project is the unordered list or ul for short. That’s why I’m going to apply our new dropdown class to the ul tags only. Any valid unordered list will do — no biggie here.

The other very important thing is that you must have some kind of CSS resetting tool that would remove the default styling of HTML elements. I am using my own reset.css file. There are other similar tools out the: on Yahoo!, Eric Myer has one too.

Now, keep in mind that you might have multiple dropdowns on the same page or other absolutely positioned elements that form the stack of layers which can sit on each other. We want to make sure that our dropdown menus don’t appear behind any such elements, so we respond to this by relatively positioning the dropdown and automatically putting it in the stack at a chosen point 596, which is pretty high and should be enough to keep it and everything it has inside on top of everything.

ul.dropdown {
 position: relative;
 z-index: 596;

Ok, great. The next thing we do is work on the inner lists. Since this structure supports hierarchies, which of course it does, normally we want only the first level list to be visible. So by default, we hide all inner list. Display parameter is set to none for this reason.

What we also do to all inner lists is, of course, position them absolutely. We basically want to give them an absolutely autonomous position with one important condition — they must generate from the list item that they belong to (hereinafter the parent). I will get back to this later in this article, but having that in mind, let’s think of the dropdown as a drawer which is not static and it can move, but at the same time it is constrained by some basic rules — you can imagine that. Well, we have rules here too, just slightly different. Rule number one is to put the dropdown just right below the parent. So the top margin will be the whole height of the parent or 100%, the left margin will equal to 0, and the width — 100%. Please note that these values correspond to the size of the parent item. Rule number two, since this is a absolutely positioned element, you have to set the stack order to 597, which is one point higher than the previously positioned root list.

ul.dropdown ul {
 display: none;
 position: absolute;
 top: 100%;
 left: 0;
 z-index: 597;
 width: 100%;

So far so good. Here comes another specific rule. Remember we just put all inner lists below their parent items? Well, it turns out that this is only good for the 1st level dropdown lists. The 2nd and all other higher level dropdowns should normally appear on the starboard side if their parents. That’s why we have to override top and left parameter values for all these lists. Again, these values correspond to the size of the parent item. 2px that you can see below is my free choice.

ul.dropdown ul ul {
 top: 2px;
 left: 100%;

Now we get to the point when we have to format the list items or li tags for short. Potentially each list item can hold a new list, it can be what I called the parent. But the absolutely positioned inside list won’t rest against this item, unless we assign to it the relative position. Such position requires us to set the z-index. The next number in the stack is 598, because we want each following list to be higher than the preceding.

ul.dropdown li {
 position: relative;
 z-index: 598;

This one below is a contingency line. I’ve seen situations in which the active list appears behind other lists of the same dropdown menu. I’m just being consistent and careful and I think this definition here makes sense:

ul.dropdown li:hover {
 z-index: 599;

Another short line here will place all top level list items in a horizontal row, but only the top level!

ul.dropdown > li {
 float: left;

Last but really not least, we do the action stuff. This line here will send a command to show the first successive list that belongs to the list item that the user has rolled over. Remember we previously hid all inner list? Well right here we do the opposite, but only to the list that has been triggered.

ul.dropdown li:hover > ul {
 display: block;

Vertical Left-to-Right Direction Dropdown Component: dropdown.vertical.css

In this section I’m going to deal with the vertical left-to-right direction dropdown menu component. It’s only a component, it relies on the main dropdown module, so the first task is to import a css file that has all the above lines that I’ve just discussed.

@import "dropdown.css";

Now, having all module lines on-board it’s time to fix this thing to our new liking. Easy. Before that, note that I’ve extended the dropdown class definition with additional class name vertical. So now all the lines in this section will work if the unordered list has both class names: dropdown and vertical.

Our vertical dropdown will of course list its items vertically. We probably don’t want it to be too wide, so the first constrain we apply is to the width. I’ve just set a default value to 200 pixels, which I think is an optimal width. It’s a free choice.

ul.dropdown.vertical {
 width: 200px;

Remember on the main module we have placed the 2nd and all other higher level dropdowns on the starboard side if its parent? In this case, all inner lists should be in this position. Same css definitions, only applies for all lists.

ul.dropdown.vertical ul {
 top: 2px;
 left: 100%;

And finally top level list items should not be in a horizontal row here. We override this definition and set the float parameter to none, like this:

ul.dropdown.vertical > li {
 float: none;

Vertical Right-to-Left Direction Dropdown Component: dropdown.vertical.rtl.css

The vertical right-to-left dropdown component extends the vertical left-to-right or the default vertical component. It’s no need to import the main dropdown module here. The default vertical component will do.

@import "dropdown.vertical.css";

I have now added the third extension or the third class name to the menu. I call it rtl, which stands for right-to-left obviously. You can see that all CSS definitions now work even under 3 conditions: (1) when the unordered list has all 3 class names: dropdown, vertical and rtl; (2) if the html tag has its dir attribute set to rlt (usually for Arabic and Hebrew support) or (3) if the unordered list itself has a dir attribute set to rtl.

Obviously, the first thing is to move the whole menu to the right side, because it reads from right to the left. Float parameter is set to right for this reason.

html[dir=rtl] ul.dropdown.vertical,
ul.dropdown.vertical[dir=rtl] {
 float: right;

Here we place all inner lists at the port side of their parent list item. We override the left parameter to auto which in the previous component was set to 100%. Finally, we do the opposite with the right margin and set it to 100% which will move the inner lists to the left of their parent items.

ul.dropdown.vertical.rtl ul,
html[dir=rtl] ul.dropdown.vertical ul,
ul.dropdown.vertical[dir=rtl] ul {
 right: 100%;
 left: auto;

Below is the last piece of code. Under the first condition the text in the list items should normally be right aligned as it would be if we had the other two conditions in effect.

ul.dropdown.vertical.rtl li {
 text-align: right;


As you can see, CSS2 provides us with all necessary tools to create a dropdown without having to code too much. What is more, we managed to keep it modular, fully optimized and clean. Actually, much of these lines work in CSS Transitional – a mode which incorporates all browsers and pays attention to annoying browser bugs. However, under CSS Transitional you still have to use JavaScript helpers to make it work. That wasn’t my intention. When IE8 is out, hopefully this will work on the whole palette of browsers.

I don’t know if you have noticed, but CSS does not have any commands to identify list items that have other lists inside. We usually want to have arrows on those list items to show the user which of them will expand. Unfortunately, as far as I know, so far CSS3 hasn’t addressed this issue and I think that they should consider adding support for that.

Preview & Download

Download the project files:

Blog navigation


Leave a Reply

  • (will not be published)