Turning a list into a navigation bar

I’ve received a couple of requests for a description of how I created the navigation bar that is currently used on this site. The CSS used isn’t all that advanced, and I hadn’t really thought about describing it in detail, but after being asked about it I decided to do a write-up.

Note: Since this article was published I have changed the markup and CSS for the navigation bar a bit, so the technique for highlighting the active link is no longer in use.

I’ve cleaned up the HTML and CSS slightly, so if you compare this to what is actually used on the site there will be some small differences. In case I have redesigned by the time you read this, check out the finished example to see what the menu looked like at the time of this writing.

The HTML

The markup is very simple. It’s an unordered list, with each link in a separate list item:

  1. <ul id="nav">
  2. <li id="nav-home"><a href="#">Home</a></li>
  3. <li id="nav-about"><a href="#">About</a></li>
  4. <li id="nav-archive"><a href="#">Archive</a></li>
  5. <li id="nav-lab"><a href="#">Lab</a></li>
  6. <li id="nav-reviews"><a href="#">Reviews</a></li>
  7. <li id="nav-contact"><a href="#">Contact</a></li>
  8. </ul>

View Step 1.

Why use a list? Because a navigation bar, or menu, is a list of links. The best (most semantic) way of marking up a list of links is to use a list element. Using a list also has the benefit of providing structure even if CSS is disabled.

At this stage, with no CSS applied, the list will look like any old (normally bulleted) list, styled only by the browser’s defaults.

I’ve given id attributes to the ul and li elements. The id attribute for the ul element is used by the CSS rules that style the entire list. The li elements have different id values to enable the use of CSS to highlight the currently selected link. This is done by specifying an id for the body element. More on that later.

The CSS

I’ll describe the CSS I’ve used to style the list in a step-by-step fashion.

First of all, I set the margins and padding of the list and list items to zero, and tell the list items to be displayed inline:

  1. #nav {
  2. margin:0;
  3. padding:0;
  4. }
  5. #nav li {
  6. display:inline;
  7. padding:0;
  8. margin:0;
  9. }

View Step 2

This will make all the links display one after another on the same line, as if the list wasn’t there. It will also remove the list bullets, since they are only displayed when display:list-item (the default display mode for list items) is used. Some browsers are said to incorrectly display the list bullets even though display:inline has been applied to the list items. I haven’t seen this happen in any of the browsers I tested in, but if you want to make sure that no browsers display list bullets, you can add list-style-type:none to the rule for #nav.

Next, it’s time to start styling the menu tabs. I do this by adding styles to the links, not to the list items. The reason for that is that I want the entire area of each tab to be clickable. First a bit of colour to make the changes more obvious:

  1. #nav a:link,
  2. #nav a:visited {
  3. color:#000;
  4. background:#b2b580;
  5. }

View Step 3.

Note that I’m styling the normal and visited states of the links to look the same. the next step is to add a bit of padding to the links:

  1. #nav a:link,
  2. #nav a:visited {
  3. color:#000;
  4. background:#b2b580;
  5. padding:20px 40px 4px 10px;
  6. }

View Step 4.

That’s a bit better. But there is a potential problem that isn’t visible here. Since the links are inline elements, their vertical padding will not add to their line height. It’s easier to see this if the ul element has a background, so I’ll add a background colour and a background image:

  1. #nav {
  2. margin:0;
  3. padding:0;
  4. background:#808259 url(nav_bg.jpg) 0 0 repeat-x;
  5. }

View Step 5.

Oops. Now the links are sticking out of the list element. To fix this, I’ve turned the links into block boxes by floating them to the left. I’ve also set their width to auto, to make them shrink to fit their content:

  1. #nav a:link,
  2. #nav a:visited {
  3. color:#000;
  4. background:#b2b580;
  5. padding:20px 40px 4px 10px;
  6. float:left;
  7. width:auto;
  8. }

View Step 6.

Adding display:block to the CSS rule for the links would also have made them block boxes, but since a floated element automatically generates a block box, that isn’t necessary.

As you may have noticed, the background disappeared when the links were floated. That’s because floated elements are taken out of the document flow, which causes the ul element containing them to have zero height. Thus, the background is there, but it isn’t visible. To make the ul enclose the links, I’ve floated that too. I’ve also set its width to 100%, making it span the whole window (except for the padding I’ve given the body element in this example):

  1. #nav {
  2. margin:0;
  3. padding:0;
  4. background:#808259 url(nav_bg.jpg) 0 0 repeat-x;
  5. float:left;
  6. width:100%;
  7. }

View Step 7.

To visually separate the links from each other, I’ve added a right border to the links. Then, to give the first link a left border as well, I’ve used a :first-child pseudo-class to apply a rule only to the link in the very first list item. I’ve also added top and bottom borders to the ul element:

  1. #nav {
  2. margin:0;
  3. padding:0;
  4. background:#808259 url(nav_bg.jpg) 0 0 repeat-x;
  5. float:left;
  6. width:100%;
  7. border:1px solid #42432d;
  8. border-width:1px 0;
  9. }
  10. #nav a:link,
  11. #nav a:visited {
  12. color:#000;
  13. background:#b2b580;
  14. padding:20px 40px 4px 10px;
  15. float:left;
  16. width:auto;
  17. border-right:1px solid #42432d;
  18. }
  19. #nav li:first-child a {
  20. border-left:1px solid #42432d;
  21. }

The :first-child pseudo-class is not recognised by Internet Explorer for Windows, so the first link won’t have a left border in that browser. In this case, that isn’t a major problem, so I’ve left it like that. If it’s really important to you, you’ll need to add a class to the first list item (or the link in it), and then use that to give the link a left border.

View Step 8.

Next I’ve changed the way the link text is displayed by removing the underlining, making the text bold, specifying font size, line-height, and a different font family, making the text uppercase, and adding a little bit of drop shadow. The drop shadow is created with the text-shadow property, a CSS3 property that is currently only supported by Safari and OmniWeb:

  1. #nav a:link,
  2. #nav a:visited {
  3. color:#000;
  4. background:#b2b580;
  5. padding:20px 40px 4px 10px;
  6. float:left;
  7. width:auto;
  8. border-right:1px solid #42432d;
  9. text-decoration:none;
  10. font:bold 1em/1em Arial, Helvetica, sans-serif;
  11. text-transform:uppercase;
  12. text-shadow: 2px 2px 2px #555;
  13. }

View Step 9.

To give some visual feedback when the links are hovered over or tabbed to, I’ve given their :hover and :focus states different text and background colours:

  1. #nav a:hover,
  2. #nav a:focus {
  3. color:#fff;
  4. background:#727454;
  5. }

View Step 10.

In the final step, I’ve added rules that will make the selected link look different than the others, to show visitors where they are on the site.

In case you haven’t seen an example of specifying an id attribute for the body element to style the “current” navigation tab differently before, that’s what the first two rules do. In the examples linked to from this article, I’ve set id of the body element to “home”, which makes the “Home” tab the current one. Changing it to “about” would make the “About” tab the current one, and so on.

I’ve also made the selected link stay the same when it’s hovered over. It can be argued that the current menu item should not be a link at all. In this case, I’ve chosen to leave the link in the markup and use CSS to remove the visual feedback on hover.

To give some visual feedback when you click on one of the links, I’ve given the :active state of the links the same styling as the selected link:

  1. #home #nav-home a,
  2. #about #nav-about a,
  3. #archive #nav-archive a,
  4. #lab #nav-lab a,
  5. #reviews #nav-reviews a,
  6. #contact #nav-contact a {
  7. background:#e35a00;
  8. color:#fff;
  9. text-shadow:none;
  10. }
  11. #home #nav-home a:hover,
  12. #about #nav-about a:hover,
  13. #archive #nav-archive a:hover,
  14. #lab #nav-lab a:hover,
  15. #reviews #nav-reviews a:hover,
  16. #contact #nav-contact a:hover {
  17. background:#e35a00;
  18. }
  19. #nav a:active {
  20. background:#e35a00;
  21. color:#fff;
  22. }

View Step 11, the finished navigation menu.

That’s it. This step-by-step tutorial makes the whole thing look more advanced than it really is. View source on the final example to see the complete set of CSS rules. By the way, with a couple of small exceptions (the left border on the first link, and the text shadow), this works in just about any browser, even Internet Explorer (version 5 or newer).

I hope you’ve been able to follow along well enough to be able to create your own navigation menu. The styling possibilities are almost endless.

Translations

This article has been translated into the following languages:

Posted on January 10, 2005 in CSS

Comments

  1. Very nice writeup. You could remove the #nav li:first-child a reference by changing the #nav border-width to this: border-width:1px 0 1px 1px;. That would put a left border on the whole list, and it would work with IE as well.

  2. There are a ton of styles for list navigation at List-a-matic, that’s where I generally borrow from.

  3. January 11, 2005 by Roger Johansson (Author comment)

    Steve: The reason I didn’t do what you’re suggesting in this case is that I’ve set set the width of the ul element to 100%. Adding a left/right border (or padding) to it would make its total width exceed 100%, which would complicate things, and potentially introduce a horizontal scrollbar.

    In fixed width situations it would work though — you’d just need to subtract the border width from the width of the ul.

  4. As a newbie, I followed your guide, but I have some problems: the hover property doesn’t seem to work. You can take a look at this

    Any help would be appreciated. Thanks!

  5. January 11, 2005 by Rob Waring

    Very useful and exactly what I was looking for for an internal site I’m doing, esp. the bit about making the whole object clickable. I assume the if I wanted the list to be vertical not hortizontal I would put something else instead of inline?

  6. Excelent writeup.

    Im currently using a similar menu for one of my own sites but i had been curious as to how to colour the tabs based on the page.

    It seems so easy know i know how.

    Thanks again for the writup, very useful.

    Regards

  7. Roger, thanks for that.

    One question: you float the <ul> Element so that it stays “open”. This was a valuable piece of information to me but I couldn’t find anything about it in the specs. Is there a rule that says that a parent element stays open when it is floated itself even though all elements inside are floated too? Or did you just happen on this?

    Thanks for the hint.

  8. January 11, 2005 by Roger Johansson (Author comment)

    Radu: The hover works for me. Which browser are you using?

    Rob: Yes, block instead of inline is one way of doing that. You’d need to change some other rules too for that to work though.

    You could also remove the float from the a elements, or make the whole ul element too narrow for two links to fit next to each other.

    Andreas: Yep. Any floated element should expand to contain any floated elements descending from it.

    Eric Meyer explains it in his article Containing Floats. I’m not sure of exactly where this is mentioned in the CSS specs; it could be in 9.1.2 Containing blocks.

  9. Roger: Good point. I rarely deal with percentages, so I tend to forget about adding borders. It makes me wish that in future versions of CSS you could combine %’s and, say, px using equations. For example, here it would work well if you could do something like

    width:100%-1px;

    Maybe years down the road.

  10. Thanks heaps for this one! It helped me heaps with a site I was working on.

    One thing I did with was add secondary sub-lists. The top list highlights as well as the sub-list to what the specific page is. I used your step 11, but used the specific child selector (>) to highlight the top list, then normal #name #othername to highlight the second.

    Hope some of that makes sense. I’ll see if I can post samples (it’s on an admin area of a site).

  11. January 14, 2005 by Roger Johansson (Author comment)

    Tarwin: That sounds interesting. If you can, go ahead and post a link to a sample.

  12. Thanks alot for this - very handy thing to know. Will definately use some of the tricks on new sites. Cheers!

  13. Hmmm…. tried to make the my top links more intelligent by using the “body id” tag so the user knows what section they’re in. Works fine in IE on PC but doesn’t work in Firefox or Safari. Can anyone tell me what I’m doing wrong?

    The website is here

  14. January 21, 2005 by Roger Johansson (Author comment)

    Tom: You have a comma that needs to be removed after the last selector in each of the groups of selectors for the menu:

    ...
    #index #nav-index a,
    

    Should be

    ....
    #index #nav-index a
    

    And likewise for the :hover rule.

    When in doubt, validate ;-)

  15. Excellent! That works a treat.

  16. Thanks for helping me get one step closer to becoming a billionaire.

  17. February 9, 2005 by Tracy

    Roger, this was fantastic. I too am a newbie to CSS and by following your guide I was able to create my nav bar using CSS. Thanks for that!

    I did notice though that my nav bar wraps as the window gets smaller. I see on your website that you added a #wrap div tag. I assume that was done to prevent the nav bar from wrapping. Can you tell me what CSS you used with the #wrap div tag (if that is indeed why you used it)?

    Thanks, Tracy

  18. February 9, 2005 by Roger Johansson (Author comment)

    Tracy: The #wrap div is used to give the whole site a fixed width. A side effect is that it prevents the navigation bar from wrapping as the window is resized. However, it doesn’t stop it from wrapping if you enlarge the text enough. You need to pump it up quite a bit but eventually, it will happen.

  19. Thanks a lot! But the left border of the first <li>cannot be seen in my IE. However, it is there in FireFox 1.01. Is there any way to satisfy most mainstream browsers?

  20. March 5, 2005 by Roger Johansson (Author comment)

    Fallfish: See comments #1 and #3. You can apply a left border to the ul element to make IE happy.

  21. March 5, 2005 by Ash

    This has been very useful, excellent write-up! One question: is there any way I can have one of the links not change colour on mouseover, and the rest change colour? I am making a site where the client has one area that he wants included in the menu, but to not be actually selectable yet?

  22. March 5, 2005 by Ash

    Oops, not to worry, just seen what I was doing wrong. Apologies!

  23. I am trying to create a navigation bar with four links. 100% Width UL, 25% Width LI, Padding 0px, Margin 0px.

    Works find in FireFox, but not Internet Explorer 6.

    Is this because of the way IE renders CSS?

  24. March 7, 2005 by Roger Johansson (Author comment)

    Gaz: Probably. What happens in IE?

  25. Hi Roger,

    It’s ok now, I set the padding and margin of the LI element to 0px.

  26. Hey Roger,

    Fantastic writeup. I’ve been looking for css ways to implement this sort of thing without any php, for some reason though I can’t seem to get the state where link to the page is highlighted if the browser is displaying that page.

    I’m just testing it out, so I’ve included for the first link only, which should ideally work right?

    www.brokenkode.com/dev

    Thanks for any help, I do honestly appreciate any feedback.

  27. yeah sorry Roger, realised that in actual fact your code works a peach, it’s just that I’ve got to come up with a way to dynamically change the body id tag for different types of pages to bring out the css…Once again thanks for the tutorial.

  28. March 30, 2005 by Christopher Zimmermann

    Thank you very much, very useful!! going to be using some of this is a site I’m building now.

  29. April 4, 2005 by Mike

    What should I add to this if I opted to not have a background behind the bar and wanted to center it on the page?

  30. Hello everyone. I need some help. My navigation bar is working fine but I can’t make the active link work so you know where you are. I doesn’t work on any of the browsers, is there any code wrong? Can anyone help me? url: www.actonit.org.uk You can see the style sheet: www.actonit.org.uk/style2.css

    Thanks.

  31. November 22, 2005 by elvis presley

    hey man, I luv css, makes me wanna bring out the old teddy bear & just ah luv cause I dont wanna be your tiger cause tiger play to rough. I dont wanna be your lion cause lions arent the kind ya! luv em so ya! just wanna be your teddy bear :)

    E

  32. Hi, I need some help with this because the floats are messing up the rest of my layout. It is pushing the main content far to the right. When I use align left, it makes the background very narrow. How to make sure the floats work well with the rest of the page?

  33. December 13, 2005 by jdwaters

    love the step-by-step modifying and results. Many tutorials do step-by-step code, but not many show the result of each modification step.

  34. Nice and clear tutorial.. We’ll wait for another tuts.. Keep a good work ! Thx.. :)

  35. Roger, I’m trying to use your HTML and CSS code to make a vertical menu. Everything seems to work fine except the selected (“active”) item.

    The selected item doesn seem to adopt the color and background as specified in the CSS:

    #nav a:active { 
    background:white; 
    color:blue; 
    }
    

    Any ideas as to what is causing that?

    The HTML is unchanged. Here’s my CSS:

    #nav { 
    margin:0; 
    padding:0; 
    width:100%; 
    } 
    #nav li { 
    /* display:inline; */
    padding:0; 
    margin:0; 
    } 
    
    #nav a:link, 
    #nav a:visited { 
    color:#000; 
    background:#b2b580; 
    font-family: Arial, Tahoma; 
    color:White;
    font-size:14px;
    font-weight:bold; 
    background-color:#50abff;
    text-decoration: none;
    padding-left: 10px;
    padding-bottom:2px;
    margin-left:0px; 
    margin-right:10px; 
    margin-bottom: 5px;
    border-bottom: 1px;
    border-bottom-color: #ffffff;
    border-bottom-style:solid;
    padding-top:4px; 
    padding-bottom:4px; 
    padding-left:14px;
    caption-side:bottom; 
    width:110px;
    } 
    #nav a:hover { 
    color:blue; 
    background:white; 
    } 
    #home #nav-home a, 
    #about #nav-about a, 
    #contact #nav-contact a,
    #archive #nav-archive a, 
    #lab #nav-lab a, 
    #reviews #nav-reviews a
     { 
    background:#50abff; 
    color:white; 
    text-shadow:none; 
    } 
    #home #nav-home a:hover, 
    #about #nav-about a:hover, 
    #contact #nav-contact a:hover,
    #archive #nav-archive a:hover, 
    #lab #nav-lab a:hover, 
    #reviews #nav-reviews a:hover
    { 
    background:gray; 
    } 
    #nav a:active { 
    background:white; 
    color:blue; 
    }
    
  36. December 27, 2005 by Roger Johansson (Author comment)

    Curt: The a:active rule is only used when a link is activated, in most cases when the user clicks on it. The styling for the selected links is defined in this rule:

    #home #nav-home a, 
    #about #nav-about a, 
    #contact #nav-contact a,
    #archive #nav-archive a, 
    #lab #nav-lab a, 
    #reviews #nav-reviews a
     { 
    background:#50abff; 
    color:white; 
    text-shadow:none; 
    }
    

    Insert your foreground and background colours in that rule and it should work.

  37. Thanks so much for the tutorial. It works like a charm; pity Tarwin never came back with the sub-menu code.

    Just one thing: once on a page, its link looks just like all the others. Not sure where I’ve gone wrong - too much grey I suppose and I got carried away.

  38. Thanks for your easy and useful tutorial! Any hope for an update on how to make this work in a sub-menu code? It seems like Tarwin never pop in with his;-)
    I’ve tried for a while to get it working with nested list but can’t get it working. Toplevel selected marks the submenu elements too, selecting submenus seems to work fin.
    Any hints her would be nice!-)

  39. January 15, 2006 by Roger Johansson (Author comment)

    I’ll put making a sub-menu on my to-do list. It will take a while before I get some spare time for it though. If you want to give it a go on your own, a good starting point may be CSS Tabs, separate lists or CSS Tabs, nested lists.

  40. Thanks for this, got it working great once, now can’t get it to display the current page colour. Was working in IE but not FF now not working in either. Here’s the style, tia:

        html,body {
        margin:0;
        padding:0;
        color:#fff;
        }
    body {
    padding:10px;
    font:small/normal "Lucida Grande", "Lucida Sans Unicode", Lucida, Verdana, Geneva, Arial, Helvetica, sans-serif;
    }
    #nav {
        margin:0;
        padding:0;
        background:#33f url(nav_bg.jpg) 0 0 repeat-x;
        width:100%;
        float:left;
        border:1px solid #33f;
        border-width:1px 0;
    }
    #nav li {
        display:inline;
        padding:0;
        margin:0;
    }
    #nav a:link,
    #nav a:visited {
        color:#fff;
        background:#33f;
        padding:5px 10px 5px 10px;
        float:left;
        width:auto;
        border-right:1px solid #33f;
        text-decoration:none;
        font:bold 1em/1em Arial, Helvetica, sans-serif;
        text-transform:lowercase;
        text-shadow: 2px 2px 2px #555;
    }
    #nav a:hover {
        color:#33f;
        background:#fff;
    }
    #nav li:first-child a {
        border-left:1px solid #33f;
    }
    #home #nav-home a
    #quotesbyauthor #nav-quotesbyauthor a
    #quotesbytopic #nav-quotesbytopic a
    #inspirationalquotes #nav-inspirationalquotes a
    #literaryquotes #nav-literaryquotes a
    #contact #nav-contact a 
    {
        background:#ffff33;
        color:#3333ff;
        text-shadow:none;
    }
    #home #nav-home a:hover
    #quotesbyauthor #nav-quotesbyauthor a:hover
    #quotesbytopic #nav-quotesbytopic a:hover
    #inspirationalquotes #nav-inspirationalquotes a:hover
    #literaryquotes #nav-literaryquotes a:hover
    #contact #nav-contact a:hover 
    {
        background:#ff3;
    }
    #nav a:active {
    background:#ffff33;
    color:#3333ff;
    }
    
  41. February 5, 2006 by Jernej

    Hello. I really liked your tutorial. But if have soem questions. Why is the active option only working in IE and not in FF. THe orange stay oranger in IE, but it just flashes for a sec in FF. And the second thing is, i have been testing out css for a while, and im wondering how you centered the whole site, so that the menu is under the banner and all that stuff.

    tnx in advance.

  42. I’m a begininer of CSS menu, and reading all the comments helped me to understand a lot.

    Since I’m trying to do a 2-level menu nav, I took the “CSS Tabs, nested lists” example and updated it with some javascript to test the menu.

    Here’s a link of my demo: http://www.kk-production.com/testmenu.htm

    My 2 questions are:

    1. how can I remove the gap between all the parent-level menu <li> item? (i.e. space between parent1 and parent2, etc)
    2. I tried changing “submenu” style into 6 different submenu, in order to use absolute position to place the submenu. But my effort failed misserably. Is there any way that I can setup absolute position of the submenu? Say when I click on parent6, I want that submenu item to be “position:absolute; left:100px” for example?

    Great tutorial guide here. Thank a lot!!!

    Kelvin

  43. I’ve always used this method to make my menues and submenues using css. In my most recent one however, I tried to make one using background images only without text for the links because I want to use a non web-safe font. It works as expected in Firefox, but in IE it flashes to the background color as you mouse over. Anyone have any idea what I did wrong or could do to fix it? It’s at http://nicolekidman.fan-wire.com

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.