Pure CSS Menu With Infinite Sub Menus Tutorial

Today we are going to cover how to build a pure CSS menu with infinite sub menus; an area of CSS that is seldom talked about, poorly written or misconceived. This pure CSS menu with infinite sub menus technique makes use of CSS2 selectors so it will not work in IE6 and below without help from JavaScript. With that said, this pure CSS menu with infinite sub menus technique will work in:

  • IE7, IE8,
  • FireFox,
  • Safari
  • Opera

This pure CSS menu with infinite sub menus technique only uses 10 CSS rules making it EXTREMELY light weight. This technique also overcomes some funky IE bugs such as Ghost Hovers and Z-Indexing issues via pure CSS.

 

Preview the example:

Lets Begin shall we? Start by creating a new html document and use the following code to make this lesson go smoother for everyone.

xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Devin R. Olsen - Pure CSS Menu Infinite Sub Menus</title>
</head>

<body>
</body>

In the body of our newly created document lets setup an unordered list with some list items like so.

<ul id="nav">
    <li>Menu 1</li>
    <li>Menu 2</li>
    <li>Menu 3</li>
    <li>Menu 4</li>
</ul>

We start by giving our top-level un-ordered element an id of “nav” so we have some sort of unique identifier to reference to with our CSS. Next lets copy the whole thing and nest a second list into any of our first level list items and change its ID to a CLASS like so:

<ul id="nav">
    <li>Menu 1
        <ul class="nav">
            <li>Menu 1</li>
            <li>Menu 2</li>
            <li>Menu 3</li>
            <li>Menu 4</li>
        <ul>
    </li>
    <li>Menu 2</li>
    <li>Menu 3</li>
    <li>Menu 4</li>
</ul>

That’s it for the HTML portion, just note that top-level list menu gets an id called “nav” while all nested sub menus get a class called “nav”. You can continue to nest as many menus you like in the same manner from here on out to meet your css menu desires.

Lets move onto the CSS portion. In the header of our document lets create some style tags that will house all our menus CSS like so:

<style type="text/css">

</style>

We will start with our most important reset and structure styles for our menu. First lets point to our #nav, .nav and #nav .nav li elements and reset their margin’s and padding’s to zero like so:

#nav, .nav, #nav .nav li { margin:0px; padding:0px; }

This is done to remove any default browser margin’s and padding’s automatically applied to these element types. Next lets point directly to our first level list items and give them some structure / style like so:

#nav, .nav, #nav .nav li { margin:0px; padding:0px; }
#nav li {float:left; display:inline; cursor:pointer; list-style:none; padding:0px 10px 0px 10px; border:1px #000 solid; position:relative;}

Our menu will be of horizontal so we set the first level list items to a float of left and a display of inline. We also give the list items a list-style of none to remove browser default bullet points next to each list item. Last we give our list items a position of relative in order to prevent any child elements who may be positioned absolute from breaking out of the flow of design. The rest of the styles are of visual preference and up to the developer to change however they desire.

Next we must have some way to identify our top-level sub menus to from all the rest. We do this to give visual guidance on how our first level sub menu is displayed against our top-level menu. To do this we must include a new class to our first level sub menus ONLY, called “first” like so:

<ul id="nav">
    <li>Menu 1
        <ul class="nav first">
            <li>Menu 1</li>
            <li>Menu 2</li>
            <li>Menu 3</li>
            <li>Menu 4</li>
        <ul>
    </li>
    <li>Menu 2</li>
    <li>Menu 3</li>
    <li>Menu 4</li>
</ul>

Again this is only for our “FIRST LEVEL SUB MENUS” that we apply this new “first” class to and not any of the rest of our further nested menus. Next lets point to these first level sub menus and set some structure rules via CSS like so:

#nav, .nav, #nav .nav li { margin:0px; padding:0px; }
#nav li {float:left; display:inline; cursor:pointer; list-style:none; padding:0px 10px 0px 10px; border:1px #000 solid; position:relative;}
#nav li ul.first {left:-1px; top:100%;}

We give our first level sub menu’s a top percentage of 100 to make it fall directly below our top-level menu. Because our list items have a 1px border we give our first level menu items a left style that is negative 1 to assure our first sub menu’s fall completely to the left when being displayed.

Next lets start giving our elements some style rules. First we will point to all our list items and anchor links that any list item may carry like so:

li, li a {color:#000;}

Here we have set our list items and anchor tags any list item may carry to have a text color of black or rather #000. Next we will point to all our sub menu list items ONLY and give some structure and styles like so:

li, li a {color:#000;}
#nav .nav li { width:100%; text-indent:10px; line-height:30px; margin-right:10px; border-top:1px #000 solid; border-bottom:1px #000 solid; border-left:none; border-right:none; background:#fff;}

Note how we first pass through the #nav element before calling .nav li vs. just stating .nav li? This is important as its a fix to helps us over come some spacing issues due to our list item borders. The first style we give here is a width of 100% to insure our list items will completely span across its parents set width. The rest of the styles are up to the developer to change to however they like.

Next lets give some structure to our list item’s anchor elements like so:

li, li a {color:#000;}
#nav .nav li { width:100%; text-indent:10px; line-height:30px; margin-right:10px; border-top:1px #000 solid; border-bottom:1px #000 solid; border-left:none; border-right:none; background:#fff;}
#nav li a {display:block; width:inherit; height:inherit;}

We first point to all our list items and then search for all anchor tags, then we give each anchor tag a display of block and an inherited width and height from its own parent element.

Next lets begin adding our menus behavior styles such has hover and when to display a sub menu and when not to display a sub menu. This part is really where the magic happens and our ability to have a pure CSS menu with infinite sub menus come into play.

First we need to give our sub menus a rule to hide them self’s from view until told otherwise like so:

ul.nav { display:none; }

We add an extra measure of security here when dealing with the display none style by stating “ONLY un-ordered list elements who have a class of “nav” must by default be a display of none”.

Next we will set our behavior color styles for list items and list item anchor tags like so:

ul.nav { display:none; }
#nav li:hover > a, #nav li:hover { color:#fff; background:#000; }

We first point to all our list items that are under a hover state, as well as all list item’s anchor tags while the list item is under a hover state. Then we set a text color of white or rather #fff and a background of black or rather #000. These styles are for the developer to change to their desired look and feel.

Next we must create a rule to display our sub menus when its parent list item is under a hovered state like so:

ul.nav { display:none; }
#nav li:hover > a, #nav li:hover { color:#fff; background:#000; }
li:hover > .nav { display:block; }

Here we are stating that all list items that are under a state of hovered who have a child element with the class of .nav (our sub menu) must be displayed. By using the CSS child selector (>) we are also stating that only the first proceeding child sub menu must be displayed while all the rest remain hidden.

Next lets talk about how we fix yet another IE bug. This one is called the “IE:Hover Ghost Bug” and in its basics it leaves deeply nested sub menus in a state of constant display after being hovered. To fix this bug we simply look at our last style as a igniting point. By giving style and structure to our sub menus when told to be displayed, vs. having a constant rule that is always giving style and structure to our element at all times, we squash the IE bug. So we add the following styles to our last rule like so:

ul.nav { display:none; }
#nav li:hover > a, #nav li:hover { color:#fff; background:#000; }
li:hover > .nav { display:block; position:absolute; width:200px; top:-2px; left:50%; z-index:1000; border:1px #000 solid;}

The position absolute is crucial to allowing our sub menus to be positioned relatively to its parents location on the page. We also set a pre-defined width to our sub menus of 200px as well as offset the menu by top -2px (due to borders width) and by left 50% of its parent element. Last and most important style we give to our sub menus is a z-index of 1000 to make sure everything layers or rather sorts correctly.

Last thing we need to do is squash yet another IE bug that deals width z-index and properly sorting elements or rather layers. To do this we simple state that all sub menu’s parent elements (list items) must have a higher z-index value than the sub menu elements like so:

ul.nav { display:none; }
#nav li:hover > a, #nav li:hover { color:#fff; background:#000; }
li:hover > .nav { display:block; position:absolute; width:200px; top:-2px; left:50%; z-index:1000; border:1px #000 solid;}
li:hover { position:relative; z-index:2000; }

Pssst.. That’s it folks, that’s all the CSS you need to have a fully compleated Pure CSS Menu with infinite amounts of sub menus, enjoy!

Completed CSS Code:

#nav, .nav, #nav .nav li { margin:0px; padding:0px; }
#nav li {float:left; display:inline; cursor:pointer; list-style:none; padding:0px 10px 0px 10px; border:1px #000 solid; position:relative;}
#nav li ul.first {left:-1px; top:100%;}

li, li a {color:#000; text-decoration:none;}
#nav .nav li { width:100%; text-indent:10px; line-height:30px; margin-right:10px; border-top:1px #000 solid; border-bottom:1px #000 solid;
border-left:none; border-right:none; background:#fff;}
#nav li a {display:block; width:inherit; height:inherit;}

ul.nav { display:none; }
#nav li:hover > a, #nav li:hover { color:#fff; background:#000; }
li:hover > .nav { display:block; position:absolute; width:200px; top:-2px; left:50%; z-index:1000; border:1px #000 solid; }
li:hover { position:relative; z-index:2000; }

Preview the example.

Thanks Everyone.
Devin R. Olsen

About Author:

I work as a full time frontend web developer at ISITE Design located in Portland Oregon. I like to teach, share and dabble deep into the digital dark arts of web and game development.

Follow Me:
TwitterFacebookGoogle Plus