Pure CSS menus

Navigation

Skip navigation.

Site search

Site navigation

Tutorial links

CSS menus

This uses just CSS 2 to turn nested lists into a working menu and does not use any JavaScript (except in IE 5.5-6 on windows where I use the proprietary DHTML behaviours, because its CSS handling is not good enough to work this menu without script)

This works in Mozilla 1+, Netscape 7+, Opera 7+, Safari build 60+, Chrome, OmniWeb 4.5+, Konqueror 3.2+, iCab 3+ and IE 5.5+ on Windows (using DHTML behaviours in IE 5.5-6)

This produces a styled nested list in IE 4, Netscape 4, OmniWeb 4.2-, iCab 2- and WebTV

This does not function as a menu or a complete list in Netscape 6, Opera 6-, Konqueror 3.0-, Konqueror 3.1 (bug), Internet Explorer on Mac, ICEbrowser, Escape and Clue browser

Note, the menu to the right plays on the beautiful design by Brother Cake.

If you are interested in making it horizontal, try CrazyTB's menu. It does not work in IE 7- (IE's CSS regularly sucks, so this is nothing new), but it does work very well in the others.

This requires browsers to be able to understand the > selector, to support :hover over li elements, to be able to correctly position li elements relatively, and ul elements absolutely, to support the display style, and to be able to change display when :hover is detected. Alternatively, a conditional comment and DHTML behaviours make it work in IE 5.5-6. See below for all the CSS used to make it work - substantially smaller than the equivalent JavaScript.

What makes this menu better than JavaScript menus?

Glad you asked. As I have already said, it is substantially smaller than the equivalent JavaScript. It does not need ANY JavaScript knowledge to set it up. Browsers that do not understand it will simply show a nested list so that the menu could still be used - with JavaScript menus, these browsers would show nothing. With many browsers, the menu will work, even if the user has disabled JavaScript. You can make any nested list into a menu simply by making its class 'makeMenu'.

To position this menu elsewhere on the page, all you need to do is make ul.makeMenu positioned using CSS positioning, and changing the colours is easy using the stylesheets and the HTC file. To make the menu open to the left instead of to the right, just change the 78px to -78px and 80px to -80px.

What makes this menu worse than JavaScript menus?

I knew you would want to know that. Because the menu relies on :hover and changing the display style on :hover, it will not work in all the browsers that a JavaScript menu can be made to work in. Even though I can make lesser browsers ignore it, there are a few browsers that understand enough CSS to make a mess, but not enough to operate the menu. If you wanted to check for these browsers, you could not do so using CSS alone and you would require some form of client sniffing using JavaScript or server side sniffing. Also, because :hover is used and not events, it is not possible to make the menus stay open for a couple of moments when the mouse moves off them - useful if you accidently move your mouse too far etc. In browsers where this menu works, it is impossible to use the menu without a mouse - a few well designed JavaScript menus do allow the menus to be operated using the keyboard alone. It may be possible to make it more keyboard accessible by using :focus as well as :hover, and setting tabindex on all of the non-linked list items (this allows browsers to focus the items), and the links.

Opera 7+
Works correctly using CSS
Opera 6
Fails because :hover is not supported
Gecko (Firefox/Mozilla/NS7+)
Works correctly using CSS
Gecko 0.9 (NS 6)
Fails because :hover is not supported
IE 8 and 9
Works correctly using CSS but has hover detection problems in IE 8 beta 1
IE 7
Works correctly using CSS
IE 5.5-6
Works correctly using DHTML behaviours (no > selector or :hover support)
IE 5.0
Fails (no > selector or :hover support, and positioning fails)
IE 4
Styled list (no > selector or :hover support)
IE Mac
Only the top level is visible (no :hover support)
Konqueror 3.2+
Works correctly using CSS
Konqueror 3.1
Should work, but fails
Konqueror 3.0-
Only the top level is visible (cannot change display on :hover)
Safari / OmniWeb 4.5+
Works correctly using CSS in build 60+
iCab 3+
Works correctly using CSS
iCab 2-
Styled list (poor CSS support)
Tkhtml
Works correctly using CSS, except that surrounding text can appear on top of the menus.
ICEbrowser
Only the top level is visible (cannot change display on :hover)
Escape
Only the top level is visible (no :hover support, and positioning fails)
Clue browser
Only the top level is visible (fails to use selectors correctly)
Netscape 4
Styled list (poor CSS support)
Omniweb 4.2-
Styled list (poor CSS support)
WebTV
Styled list (poor CSS support)
Non CSS browsers
Unstyled list

Note that as long as you do not use destructive CSS, the LIs can contain links, even if they also have submenus. This means that you can set them up as a basic navigation bar, to provide fallback for browsers that can only show the topmost level, or for users that cannot use a mouse (such as those using screenreaders).

How it works

The HTML is defined as follows for the green menu (note the use of the class attribute):

<ul class="makeMenu">
  <li>&gt;&gt; Lips
    <ul>
      <li><a href="cat.html">Cat</a></li>
      <li><a href="rabbit.html">Rabbit</a></li>
      <li><a href="dingo.html">Dingo</a></li>
    </ul>
  </li>
  <li>&gt;&gt; Ears
    <ul>
      <li>Elephant &gt;&gt;
        <ul>
          <li><a href="indian.html">Indian</a></li>
          <li><a href="african.html">African</a></li>
        </ul>
      </li>
      <li><a href="monkey.html">Monkey</a></li>
      <li><a href="dog.html">Dog</a></li>
    </ul>
  </li>
  <li>&gt;&gt; Eyes
    <ul>
      <li><a href="pig.html">Pig</a></li>
      <li><a href="bird.html">Bird</a></li>
      <li><a href="worm.html">Worm</a></li>
    </ul>
  </li>
  <li><a href="accessibleLink.html">&gt;&gt; Noses</a>
    <ul>
      <li><a href="bat.html">Bat</a></li>
      <li><a href="fish.html">Fish</a></li>
      <li><a href="panther.html">Panther</a></li>
    </ul>
  </li>
</ul>

The CSS is defined as follows (for the green menu). For practical uses, you would remove the comments:

NOTE: it is vital that the ULs are given a background colour, or Internet Explorer does not recognise mouseovers / mouseouts correctly. It is also vital that you use a DOCTYPE that triggers IE's "standards" rendering mode, or the menu will not function in IE 7.

<style type="text/css">
ul.makeMenu, ul.makeMenu ul {
  width: 80px;                 /* sets the size of the menu blocks */
  border: 1px solid #000;      /* puts a black border around the menu blocks */
  background-color: #8aa;      /* makes the menu blocks mint green - a bg-color MUST be included for IE to work properly! */
  padding-left: 0px;           /* stops the usual indent from ul */
  cursor: default;             /* gives an arrow cursor */
  margin-left: 0px;            /* Opera 7 final's margin and margin-box model cause problems */
}
ul.makeMenu li {
  list-style-type: none;       /* removes the bullet points */
  margin: 0px;                 /* Opera 7 puts large spacings between li elements */
  position: relative;          /* makes the menu blocks be positioned relative to their parent menu item
                                  the lack of offset makes these appear normal, but it will make a difference
                                  to the absolutely positioned child blocks */
  color: #fff;                 /* sets the default font colour to white */
}
ul.makeMenu li > ul {          /* using the > selector prevents many lesser browsers (and IE - see below) hiding child ULs */
  display: none;               /* hides child menu blocks - one of the most important declarations */
  position: absolute;          /* make child blocks hover without leaving space for them */
  top: 2px;                    /* position slightly lower than the parent menu item */
  left: 80px;                  /* this must not be more than the width of the parent block, or the mouse will
                                  have to move off the element to move between blocks, and the menu will close */
}
ul.makeMenu li:hover, ul.makeMenu li.CSStoHighlight {
  background-color: #ffa;      /* gives the active menu items a yellow background */
  color: #000;                 /* makes the active menu item text black */ 
}
ul.makeMenu ul.CSStoShow {     /* must not be combined with the next rule or IE gets confused */
  display: block;              /* specially to go with the className changes in the behaviour file */
}
ul.makeMenu li:hover > ul {    /* one of the most important declarations - the browser must detect hovering over arbitrary elements
                                  the > targets only the child ul, not any child uls of that child ul */
  display: block;              /* makes the child block visible - one of the most important declarations */
}
/* and some link styles */
ul.makeMenu li a { color: #fff; display: block; width: 100%; text-decoration: underline; }
ul.makeMenu li a:hover, ul.makeMenu li a.CSStoHighLink { color: #000; }
ul.makeMenu li:hover > a { color: #000; } /* supports links in branch headings - should not be display: block; */
</style>
<!--[if gt IE 5.0]><![if lt IE 7]>
<style type="text/css">
/* that IE 5+ conditional comment makes this only visible in IE 5+ */
ul.makeMenu li {  /* the behaviour to mimic the li:hover rules in IE 5+ */
  behavior: url( IEmen.htc );
}
ul.makeMenu ul {  /* copy of above declaration without the > selector, except left position is wrong */
  display: none; position: absolute; top: 2px; left: 78px;
}
</style>
<![endif]><![endif]-->

The behaviour file IEmen.htc is defined as follows):

<attach event="onmouseover" handler="rollOver" />
<attach event="onmouseout" handler="rollOff" />
<script type="text/javascript">
function rollOver() {
  //change the colour
  element.className += (element.className?' ':'') + 'CSStoHighlight';
  //change display of child
  for( var x = 0; element.childNodes[x]; x++ ){
    if( element.childNodes[x].tagName == 'UL' ) { element.childNodes[x].className += (element.childNodes[x].className?' ':'') + 'CSStoShow'; }
    if( element.childNodes[x].tagName == 'A' ) { element.childNodes[x].className += (element.childNodes[x].className?' ':'') + 'CSStoHighLink'; }
  }
}

function rollOff() {
  //change the colour
  element.className = element.className.replace(/ ?CSStoHighlight$/,'');
  //change display of child
  for( var x = 0; element.childNodes[x]; x++ ){
    if( element.childNodes[x].tagName == 'UL' ) { element.childNodes[x].className = element.childNodes[x].className.replace(/ ?CSStoShow$/,''); }
    if( element.childNodes[x].tagName == 'A' ) { element.childNodes[x].className = element.childNodes[x].className.replace(/ ?CSStoHighLink$/,''); }
  }
}
</script>

If background images are used on the menu items, then IE 5-6 will need the fix for flickering.

Important: The behaviour fix that makes this menu work in IE 5-6 is deprecated and is no longer supported (meaning that I will not help you to get it working with your pages). It was only written to fill the gap while waiting for Internet Explorer to support CSS 2 selectors and :hover on non-links. That has now happened in IE 7. Although this fix can make it work in IE 5-6 as well, that is counter productive for the future of the Web. IE 6 is a major problem to Web developers (IE 7 is a problem as well, but for now, let's overlook that, since it does at least implement the required parts well enough for this menu), and the sooner it stops being used, the better.

Instead of using this hack, users of IE 6 should be encouraged to upgrade to IE 7. Users who cannot upgrade to IE 7 (because IE 7 is not being released for most Windows operating systems) should use a better browser, such as Opera or Firefox. They have been abandoned by Microsoft.

Credit where credit's due

This menu was inspired by (and the basic technique taken from) Eric Meyer's Pure CSS Menus. The idea of using DHTML behaviours and conditional comments was inspired by Brother Cake's CSS / DHTML Hybrid Navigation Bar. Brother Cake's page also shows another way to do this. Instead of using DHTML behaviours, he uses regular JavaScript. Although this does make the scripting significantly more complex, this still retains the advantages of CSS menus, but also extends support to Internet Explorer on Mac and ICEbrowser, as long as JavaScript is enabled. I fully support this idea, but I have not yet written my own version of the required script.

This site was created by Mark "Tarquin" Wilton-Jones.
Don't click this link unless you want to be banned from our site.