Ultra-efficient CSS/HTML drop menu: Nekkidblogger menu

by Peter

Introduction

Over the last couple of years there has been an increasing focus on Web page speed. Partly this is due to initiatives by Google and Yahoo, partly it is due to the explosive increase in web browsing on devices such as iPads and advanced cell phones (iPhone, Androids, etc).

Gradually, the focus on speed has also resulted in increased attention being devoted to more efficient CSS. Efficient HTML and CSS can help improve rendering speed significantly, not least on mobile devices with slower processors. I have done a lot of work to speed up web sites lately, and in the process I have noticed that menu systems is an area where there is a lot of inefficient CSS floating around. It turns out there is lots of room for improvements.

Markup (HTML)

What we want is a HTML/CSS menu with very efficient CSS. In the example I use a horizontal 4-level dropdown menu.

The markup is basically a standard unordered list (<ul><li>). I have highlighted the non-standard features. It has id="nekkidblogger" and each of the sub-levels (nested lists) have classes describing the level: level2, level3 and level4.

Usually lists used in menus are not styled with classes like this. However, doing it this way has some great advantages: It makes it easy to build a CSS menu with very few style rules; it allows for very efficient CSS; and provides (as a kind of bonus) convenient "hooks" (i.e. the "level" classes") for styling if one wants to style the dropdown levels differently from the top level.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<ul id="nekkidblogger">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a>
  <ul class="level2">
    <li><a href="#">Link 2-1</a></li>
    <li><a href="#">Link 2-2</a></li>
    <li><a href="#">Link 2-3</a></li>
  </ul></li>
<li><a href="#">Link 3</a>
  <ul class="level2">
    <li><a href="#">Link 3-1</a></li>
    <li><a href="#">Link 3-2</a>
       <ul class="level3">
         <li><a href="#">Link 3-2-1</a></li>
         <li><a href="#">Link 3-2-2</a>
            <ul class="level4">
              <li><a href="#">Link 3-2-2-1</a></li>
              <li><a href="#">Link 3-2-2-2</a></li>
            </ul></li></ul></li>
  </ul></li><li><a href="#">Link 4</a></li>
</ul>

On the right it is shown without styling. This is a standard unordered list. The <ul> for the overall menu has id=”nekkidblogger”. It has four top level links, and under Link 3 there are three dropdown levels. This is more than sufficient for a demonstration – there is no need to fill it up with lots of dropdown elements.

So now we are ready to give it structure and style!

CSS for the Nekkidblogger menu

One of the keys to efficient CSS is to use the "cascade" as much as possible – to allow "child-elements" to inherit as much as possible from "parent" elements. So here I don’t define selectors to the extent that I can avoid it. Also, I try to only style elements once, and then let inheritance take care of as much as possible.

In the following I mark the rules that are structural (makes the menu work as it should) as “Rule” and the presentational style rules as “Style” in order to clearly separate the two.

#nekkidblogger ul {padding:0;margin:0;} (Style 1)

This isn’t really part of the menu, but useful for setting the context. If you use a CSS reset, you don’t need this. Also, you may prefer simply ul, li {padding:0;margin:0;}. I use the more specific reset here (for the "nekkidblogger"-id only) since this is a blog where the general rules have been set for other purposes.

The Top level

#nekkidblogger li { list-style:none; position:relative; float:left;
    text-align:center; margin-right:5px; font: 10px verdana, 
    sans-serif; } (Rule 1)

This is the first structural rule of the menu. Here "list-style", "position" and "float" are necessary structural elements, while "text-align" and "margin" are presentational. Note that I style the a-selector, not the <li>. The <li>-element adapts to its content – so we don’t need to define a "width" here for example.

#nekkidblogger a { display: block; width: 8em; color: black; 
     line-height: 30px; text-decoration: none; background: silver; 
     border:1px solid #fff; margin: 0 -1px -1px 0; } (Style 2)

Here everything is presentational. "display:block" is important in order to display the links as boxes. "width", "height" and the rest is styling. I set the properties of the links here, and then they are inherited by the sub-levels. In your own menu, you may want to style the dropdowns as well.

#nekkidblogger :hover > a { background:#7B7B7B; 
         color:yellow } (Style 3)

This is a presentational element only – we don’t need to set different properties for the hover state – but most often one does that in menus to make them look better. Here I style the hovered link as well as the whole hover path instead of just the hovered link (one usually uses simply "a:hover"). I picked up and adapted this neat little trick from the excellent CSS experiments by Stu Nicolls.

What we have done so far (we have also "set the stage" for the lower levels):

Second and lower levels

The rest is purely structural – no presentational elements, as I simply let the lower levels of the menu inherit everything, including the hover state (see Style 3). However, I will show you how you easily can style the lower levels yourself if you want to:

A. Style the boxes

The classes ".level2-.level(n)" provide "hooks" that you can easily use to format the lower levels.

We could, for instance, give a different style to level two and the lower levels – just style ".level2" and let the lower levels inherit. Or we could style each level separately. For instance:

#nekkidblogger .level2 a { background: #FFF; height: 25px; etc .. }

And/or, we could style the lower levels like so:

#nekkidblogger .level3 a { ... }
#nekkidblogger .level4 a { ... }

OK. Back to the nekkidblogger menu:

B. Position the dropdown boxes

The top level is horizontal. We want to drop the second level down vertically from it. We have defined the <li>’s to be relative, so now we position the dropdown (.level2) in relation to them:

.level2 { position:absolute; top:30px; left:0;  }  (Rule 2)

Here "left:0" is default in the good browsers, so we should not need to specify it, but as it for some reason is not so in IE (surprise?), we still must. 30px is the line-height – see Style 2.

The next two levels – level 3 and level 4 – we want to fly out to the right of level 2. 8 em is the width of the a-element (see Style 2). I add .08em (about 1px – the width of the border) for good alignment. So:

.level3,.level4 { position:absolute; top:0; left:8.08em; } (Rule 3)

C. Dropdowns: Hide and make visible when hover

.level2,.level3,.level4 {visibility:hidden} (Rule 4)

We need to hide all levels separately – if we let "hidden"-property be inherited, then the "visible"-property will be too, and all levels would show up when we hovered a given level. Also note that if you want to add another level or two to the menu, all you need is to insert the correct classes in the HTML for the list and then add ".level5, .level6" in Rules 3 and 4. So adding N more levels, using the Nekkidblogger menu framework, does not add to the complexity of the CSS.

#nekkidblogger :hover > ul { visibility:visible; }(Rule 5)

Makes the child elements of a <ul> visible when <li> is hovered (but only the child-elements, not the "grand-children"). And that’s all!

Here is the menu:

Final remarks

So, that’s it. Five "structural" CSS-rules for a multi-level menu (The “Style”-rules are purely presentational). The rules are also comparatively very simple, mostly very short, and computationally effective (good for fast page rendering) with minimum use of descendant selectors.

If you examine the live demo with Google’s Page Speed (using Firefox), and look at the use of efficient selectors, you will find that it has "0 very inefficient rules, 0 inefficient rules, and 2 potentially inefficient uses of :hover". The "potentially inefficient ones are Style 3 and Rule 5, but they are as simple as can be.

If by comparison you run Page Speed on the Son of Suckerfish menu with three dropdowns, which is a very widely used menu and which was published in A List Apart, you will find that it has "7 very inefficient rules, 5 inefficient rules, and 5 potentially inefficient uses of :hover". So, there is a difference. And if Google’s Page Speed is worth anything, and if page rendering speed matters, it may well be a difference that matters.

The Nekkidblogger menu has been extensively tested, and works in all major browsers: Chrome, Firefox, Internet Explorer from IE 7 and up, Safari, and Opera. In the demo I give some more detail and show the fixes necessary to make it work in IE 6.

See Copyright and Terms and Conditions, on demo page, sidebar 1.

PS: In later posts I’ll show how to make the rightmost flyouts fly left and how to convert this horizontal menu into a vertical CSS menu by changing only tiny bits of CSS.

Related Posts:

{ 6 comments… read them below or add one }

Paul Appleby

This line:
#nekkidblogger :hover > ul { visibility:visible; }(Rule 5)

Should be:
#nekkidblogger li:hover > ul { visibility:visible; }(Rule 5)

Peter

Actually both are possible and both work. “#nekkidblogger :hover > ul { visibility:visible; } is the shortest. You can see it in action in the demo:

http://nekkidblogger.com/nekkidblogger-menu.html

Best,
Peter

Paul Appleby

It didn’t work for me.

Could please explain the notation ” :hover > ul”.

Paul Appleby

I wonder why the code won’t work in IE 9 when using:
DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”
But will work when using
DOCTYPE HTML
OR
DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”
Chrome doesn’t care.

Peter

“:hover > ul”{ … } is a CSS rule saying that for all hovered element with child ul, apply definition { … }

When you say it doesn’t work, what doesn’t work? The demo? What in the demo? What specifically is it you hover that doesn’t work? In which browser and browser version and running on which operating system / version (Windows, OS X, Linux, etc)?

Peter

Peter

Again – what doesn’t work? When you do what? And operating system & version, please?

Peter

Leave a Comment