Creating indented navigation lists

As a Web site's navigation lists get longer, it often helps for Web builders to group some of the links into categories. Michael Meadhra offers some tricks to styling those lists to achieve the desired rollover effects in a navigation bar.

A vertical navigation bar is a familiar feature on many Web sites—and no wonder, it's a great way to organize a list of links. At its simplest, the navigation bar may be a list of plain text links. However, it's increasingly common for Web builders to make the list items behave like buttons, complete with CSS rollover effects that make the full button face clickable.

As navigation lists get longer, it often helps to group some of the links into categories, which are usually indicated by outline-style indents of those list items. The basic indented list structure is relatively easy to build with nested lists, but there are some tricks to styling those lists to achieve the desired rollover effects in a navigation bar.

What we're trying to accomplish

The goal is to produce a navigation bar that includes links arranged in an outline-style hierarchy as shown in Figure A. Each top-level list item is a fully clickable button with rollover effects. Indented under the category items are more fully clickable list items, also with rollover effects. In this case, the category items aren't links (they're just labels for the indented sub-list items), but adding links to these items would be a trivial change. (Note: None of the links in Figure A go anywhere. They exist solely to demonstrate the rollover effects.)

Building the navigation bar lists

There are several different ways that you can code navigation bars, but perhaps the most common approach is to mark up the links as an unordered list. After all, a navigation bar is essentially a list of links, and the <ul> and <li> tags are designed to create the appropriate structure for such a list. Marking up your navigation bar as an unordered list is especially useful when you want to create the indented effect shown in Figure A, because the XHTML standards specifically allow for nesting lists within lists. In addition, the markup for an unordered list usually ends up being a little cleaner than when you try to accomplish the same thing with <div> or <p> tags.

The following code excerpt shows the XHTML code for the sample page in Figure A. (Note: The code in this column has been tested in the current Windows versions of IE, Firefox, Netscape, Mozilla, and Opera. The underlying technique has also been tested in IE/Mac and Safari.)

<div id="header">
  <h1>Header</h1>
</div>
<div id="nav">
  <ul>
    <li><a href="#nowhere">List item 1</a></li>
    <li><a href="#nowhere">List item 2</a></li>
    <li><a href="#nowhere">List item 3</a></li>
    <li class="category">List Category 1
      <ul>
        <li><a href="#nowhere">Sub-List 1 item 1</a></li>
        <li><a href="#nowhere">Sub-List 1 item 2</a></li>
      </ul>
    </li>
    <li><a href="#nowhere">List item 4</a></li>
    <li class="category">List Category 2
      <ul>
        <li><a href="#nowhere">Sub-List 2 item 1</a></li>
        <li><a href="#nowhere">Sub-List 2 item 2</a></li>
        <li><a href="#nowhere">Sub-List 2 item 3</a></li>
      </ul>
    </li>
    <li><a href="#nowhere">List item 5</a></li>
  </ul>
</div>
<div id="main">
  <h2>Main Content</h2>
  <p>It is the star...</p>
  <p>...even to the edge of doom.</p>
</div>

The navigation bar is contained in a div with the ID nav. We'll use that ID to select the list tags within the div for styling without affecting other lists located elsewhere in the document. The navigation bar itself is a standard unordered list with sub-lists nested within the category list items.

The nested <ul> tags are completely contained within the parent <li></li> tags in order to conform to proper nesting rules. Previously, it was common practice to place the nested <ul> tags between the previous list item's </li> tag and the following list item's <li> tag rather than within the parent <li>. That old-style nesting may display as expected in some current browsers, but the code won't validate to current standards, and it may not work in future browser versions.

The only other significant thing about the markup is the class applied to the category list items that contain nested lists. We'll use that class to select those list items for special formatting.

Styling the lists as a navigation bar

It's the CSS styles that transform this list markup into a functional navigation bar. The following CSS code excerpt from Figure A demonstrates how to style a nested list and produce the rollover effects we want. (For the sake of brevity, I omitted the styles that don't pertain to the navigation bar technique.)

#nav {
    position:absolute;
    left:10px;
    top:120px;
    width:200px;
    background-color:#FFFF99;
    height:300px;
}
#nav ul {
    list-style-type:none;
    margin:0px;
    padding:0px;
    text-indent:0px;
}
#nav li {
    height:24px;
    line-height:24px;
}
#nav li {
    width:190px;
}
#nav li ul li {
    width:170px;
}
#nav li.category {
    padding-left:10px;
    height:auto;
    color:blue;
}
/*IE needs extra margin and padding set for the indented li */
* html #nav li ul li {
    margin-left:-10px;
    padding-left:10px;
}
/* end IE hack */

#nav li a,
#nav li ul li a {
    font-weight:normal;
    text-decoration:none;
    display:block;
    height:100%;
    width:100%;
    padding-left:10px;
}
#nav li.category ul li a {
    margin-left:-10px;
    padding-left:30px;
}
#nav li a:link,
#nav li a:visited {
    color:blue;
    background-color:#FFFF99;
}
#nav li a:hover,
#nav li a:active {
    color:white;
    background-color:blue;
}

The #nav style controls the size and position of the div containing the navigation bar. Note the width:200px rule. Some of the other styles contain width rules that must be calculated to fit properly within this dimension.

The #nav ul style simply cancels out the default list styling for the navigation bar lists. This allows us to style the lists without fighting unintended inherited properties.

The #nav li style sets the height of each list item (height:24px) and centers the text vertically within that height (line-height:24px). Note that this is a separate style that addresses the list item tag (li) instead of including these rules in the #nav ul style. This allows us to set a different height for the category list items with the #nav li.category style below.

The second #nav li style sets the width of the first level list items to a value equal to the width of the div minus the padding-left value for those list items. Similarly, the #nav li ul li style sets the width of the indented list items, which is reduced by total indent amount.

The category list items don't contain links, so the #nav li.category style sets the padding and color on those items to match the formatting that the other first-level list items will get from the other styles below. In addition, the height:auto rule allows the category list items to expand to accommodate the nested list. Without this rule, the nested list items would overlap the following first-level list items in most standards-compliant browsers (but not in IE).

Speaking of IE, the perpetual problem child needs a little extra help to get the indented list items properly positioned. So the * html #nav li ul li style includes negative left margin (margin-left:-10px) and positive left padding values (padding-left:10px) equal to the left padding for the category list items. The star-html selector feeds these values to IE but not to other browsers.

The #nav li a, #nav li ul li a style contains the typical rules for links in a button bar. The font-weight:normal and text-decoration:none rules counteract default styles for links that we don't need in a navigation bar. The display:block, height:100%, and width:100% rules make the full block clickable. And the padding-left:10px rule provides a small left margin for first-level list items that is also clickable. (Using margin-left:10px would create the same margin, but it wouldn't be clickable.)

The #nav li.category ul li a style establishes the indent for the nested list items. First, the margin-left:-10px rule reverses the indent inherited from the parent list item; then, the padding-left:30px sets the full indent for the indented list items. Using this combination of a negative margin and padding equal to the combined indent allows the clickable area (and rollover background color) to extend all the way to the left edge of the div instead of stopping inside the padding of the parent list item.

The remaining styles set the foreground and background colors for the various button states. For simplicity, I used the same colors for both the link and visited states and also for both the hover and active states.

Although this example shows only one level of indent for the nested sub-lists, you could easily expand the basic technique to include two, three, or more indent levels. You could also add links to the category list items.