Building an Interactive Navigation Bar with HTML/CSS

August 29th, 2013

A straightforward navigation structure can increase the usability of your site, and make information easy to find. By using basic HTML and CSS, you can build a horizontal nav bar that provides interaction when a user hovers over a navigation link.

Hey, not THAT kind of bar…

Start with Markup First

While you probably already have a design in mind that fits with the rest of your site, concentrating on HTML first, before worrying about the presentation and CSS, will help you keep your markup clean and tight. This results in better front-end performance and aids in organization as you continue your development. Figure 1 illustrates the end result.

Completed navigation bar
Figure 1. Completed navigation bar

Break It Down

You’ll learn the rationale and code for each step in the build process:

  • HTML structure
  • Structural styles: aligning the items, padding, and margins
  • Decorative styles: gradients, text color, border radius
  • Special interactive styles using pseudo class selectors

HTML Structure

Solid HTML structure is the foundation of any Web page. There are many different ways to achieve the same visual result, but some structures offer advantages like semantic meaning and can help you keep your corresponding CSS stylesheets in order.

Using HTML5 Semantic Tags

Before HTML5, the tag <div> was a one-size-fits-all containing element for anything from main text sections to footers. However, new semantic tags like <header>, <nav>, <section> and <footer> better organize markup into meaningful sections and reduce the need for repetitive class names in your stylesheets. Since most modern browsers support HTML5, it’s best to take advantage of these new descriptive tags to give your markup contextual meaning.

Semantic markup not only keeps the document tree neat and tidy, it assists users who experience the Web site through means other than pixels in a browser window, such as a screen reader or other assistive device. Additionally, having sound markup structure is efficient and can eliminate the need for class names (for example, instead of <div class=”list”>, use <ul>) and therefore keep your CSS files smaller and better performing.

What About Compatibility?

If you’re working on a project that requires support for older browsers like IE7 and IE8, you’ll need to include a small script called html5shiv (or html5shim) in a conditional comment in your <head>. This script allows legacy version of IE to use html5 and provides some basic styling. It works well out-of-the-box, so there’s little need for configuration.

Include the script in your<head> file in a conditional comment targeting IE9 and lower.

  1. <!--[if lt IE 9]>
  2.   <script src="dist/html5shiv.js"></script>
  3.  <![endif]-->

Make sure that you include this file after you link to your stylesheets. Otherwise, there may be a flash of unstyled content on the screen while the script loads. To help prevent high load times, it’s best to minify the shiv script, using a tool like JSCompress.

Marking up the Navigation Bar

At its core, a navigation bar is a simple list of links. HTML has several list types: an ordered list <ol>, unordered list <ul>, and definition list <dl>. Within each list, items are wrapped in list item tags, <li>, which in this case will contain anchors that link to other pages.

Since this list serves the purpose of navigation, a <nav> tag is appropriate as the containing parent element. Then, because the links don’t necessarily belong in any numbered order, <ul> is the best choice for list type. Within the <ul>, each navigation link is housed in an <li>. Each link, or <a>, points to a link, which is defined by the using the anchors href attribute. This reference can be either an absolute or relative path. An absolute path is a full URL like “” With site navigation, you’re more likely to use a relative link, which is a reference to a file within the same directory as the referring page, like “/contact.”

  1. <a href=”/contact”>Contact</a>

You can also use ../ to move back a folder.

Additionally, you can specify the link’s target, or where the link should open: in a new tab, a new window, or in the current window. Since navigation bars typically only contain links within the same website, specifying a target isn’t necessary except for special cases.

Your markup will look something like this:

  1. <nav>
  2.   <ul>
  3.     <li>
  4.         <a href="/">Home</a>
  5.     </li>
  6.     <li>
  7.         <a href="/print">Print Design</a>
  8.     </li>
  9.     <li>
  10.         <a href="/web">Web Design</a>
  11.     </li>
  12.     <li>
  13.         <a href="/bio">Bio</a>
  14.     </li>
  15.     <li>
  16.         <a href="/contact">Contact</a>
  17.     </li>
  18.   </ul>
  19. </nav>

Why Not Just Anchors?

It’s true: you could easily reproduce the same visual style and behavior with the following markup:

  1. <div class=”nav”>
  2.   <a href="#">Home</a>
  3.   <a href="#">Print Design</a>
  4.   <a href="#">Web Design</a>
  5.   <a href="#">Bio</a>
  6.   <a href="#">Contact</a>
  7. </div>

This is even less markup than the example provided above. But imagine this snippet of code was taken out of context. Sure, it would still be a collection of links. However, it wouldn’t be apparent that these particular links were the main navigation for the page, or even that they should appear within the context of a list. Now imagine you’re using a screen reader and can’t see the rest of the page. Using semantic tags, though it may cause another level of nesting in your markup, actually creates a better-organized and accessible page. Additionally, using semantic tags like <nav> actually negate the need for a class name like “nav,” therefore reducing the complexity of your site’s markup.

Styling with CSS

Now that the HTML is in place, you can use CSS to arrange and decorate each element.

Many CSS frameworks advocate separating structural CSS, like alignment, margins, and padding, from decorative CSS, like gradients, text color, and rounded corners. While this approach may or may not suit your project, we’ll keep these two types of CSS separated to better understand the styles being applied to the navigation bar.

Reset Stylesheet

In any project, using a good reset stylesheet can save you from overriding browser-specific default style behaviors in multiple places.  A very basic reset stylesheet might look something like the following:

  1. h1, h2, h3, h4, h5, h6, p, a, ul, ol, li, small {
  2.   margin: 0;
  3.   padding: 0;
  4.   border: 0;
  5.   outline: 0;
  6.   font-size: 100%;
  7.   background: transparent; }

Essentially, a reset stylesheet simply sets each element back to a common default. This prevents you from having to manually override default structural or decorative rules in your individual style declarations, since browsers often apply their own styles and formats to the above elements, causing them to look inconsistent across browsers.

Eric Meyer’s reset stylesheet is a popular choice for many projects, and Richard Clark updated it to add HTML5 element.

Whatever type of reset stylesheet you use, make sure that it’s the first stylesheet added to your head. That way your site will take advantage of CSS’s cascading behavior, and your subsequent stylesheets will simply modify an already-consistent common style.

Structural CSS: Width, float, and spacing

First start by declaring a width and some margin spacing for the navigation bar.

  1. /* Give the body a width */
  2. body {
  3.   width: 100%;
  4.   max-width: 960px;
  5.   margin: 0 auto; }
  6. /* Make the nav take up the whole body width, and give it some top and bottom margin space */
  7. nav {
  8.   width: 100%;
  9.   margin: 20px 0; }
  10. /* Make the list of links take up the whole width of the nav */
  11. nav ul {
  12.   width: 100%; }

There are five navigation links, so they each need to take up 1/5 of the total nav bar width. This means each <li> should be 20% wide, and the anchors inside of them should take up 100% of the <li>. We also want to set the anchor display to block, since we haven’t declared that yet. Making the anchor a block will make it fill the height and width of its parent <li>. Depending on your reset stylesheet, you may not need to declare this rule again.

  1. nav ul li {
  2.   width: 20%; }
  3. nav ul li a {
  4.   display: block;
  5.   width: 100%; }

While you may prefer that lists in other parts of your site to be visually distinguishable with bullets, dashes, or discs for each list item, you definitely don’t want those to show up in the navigation bar. Adding the following style rules will override any existing list-item or spacing styles being applied. Note: if you are using a reset stylesheet, these rules may not be necessary.

  1. nav ul {
  2.   width: 100%; /* from above */
  3.   list-style: none;
  4.   margin: 0;
  5.   padding: 0; }

The last piece of the structural CSS is getting each of the links to line up side by side, creating the horizontal bar. By default, each of the <li> elements will stack vertically as Figure 2illustrates.

The navigation links will stack vertically by default
Figure 2. The navigation links will stack vertically by default

To make them appear in a horizontal row, use float.

  1. nav ul li {
  2.   width: 20%; /* from above */
  3.   float: left; }

Floating left will stack items from left to right, starting with the first item in your list. Since there are five items that take up 20% of the nav bar’s width, the end result, Figure 3, is a horizontal bar with five equally spaced items.

Horizontal bar with each li floated left
Figure 3. Horizontal bar with each <li> floated left

One unique characteristic of float is that it takes elements out of the page flow and pulls them either left or right. Because of this, the <ul> thinks it has no children within it, and will collapse. This issue exists with all parent/child relationships when the children are floated, not just in the case of your nav bar. To trick the parent element into recognizing its floated children, declare overflow: hidden on the parent. Your <ul> height will again be correct. You may not have any styles applied to the <ul>, but ignoring the height may lead to other layout problems. For example, if you leave the <ul> with a height of 0, subsequent elements may be spaced incorrectly (see Figure 4 for an example).

Not declaring overflow: hidden on the parent element of floated children can cause layout problems
Figure 4. Not declaring overflow: hidden on the parent element of floated children can cause layout problems

If float’s a pain, why not use inline block?

It’s true that display: inline-block will also make each of the <li> elements stack next to each other in a horizontal row. However, inline-block also adds some default space between elements (see Figure 5) that is difficult to override consistently in browsers.

Items aligned with inline-block instead of float
Figure 5. Items aligned with inline-block instead of float

Since the design of this particular nav bar doesn’t have any space between items, it’s better to use float because it’s guaranteed to stack the <li>s next to each other without any unexpected gaps.

Decorative CSS

Now that each of the HTML blocks is properly spaced and positioned, it’s time to decorate.

Each nav item has a slight gradient background, and the gradient darkens when a user hovers over the link. The first and last items also have rounded corners. CSS3 makes both of these previously-painful designs easy to implement.


Instead of using a repeating background image for gradients, most modern browsers now support CSS3 gradients, which creates the gradient from your input and uses it as a background-image. Writing your own CSS for linear or radial gradients is a bit of work, but it’s not always necessary. There are plenty of gradient generators online, and ColorZilla (a browser plugin that allows you to copy color codes from sites) has a great one of their own. If you prefer working with Sass, you can toggle the code output.

Since the anchor is the element that the users are interacting with, not the li, it’s better to make style rules for the anchor itself. It would also be possible to add style rules to the <nav> itself, but depending on your style choices, you may be setting yourself up for conflicts in the future.

Start by adding the default gradient background to the links.

  1. nav ul li a {
  2.   text-align: center;
  3.   padding: 8px 0;
  4.   display: block;
  5.   width: 100%;
  6.   background: #cdeb8e; /* Old browsers */
  7.   background: -moz-linear-gradient(top,
  8.     #cdeb8e 0%, #b0ca34 100%); /* FF3.6+ */
  9.   background: -webkit-gradient(linear, 
  10.     left top, left bottom, color-stop(0%,#cdeb8e),
  11.     color-stop(100%,#b0ca34)); /* Chrome,Safari4+ */
  12.   background: -webkit-linear-gradient(top,  
  13.     #cdeb8e 0%,#b0ca34 100%); /* Chrome10+,Safari5.1+ */
  14.   background: -o-linear-gradient(top,  
  15.     #cdeb8e 0%,#b0ca34 100%); /* Opera 11.10+ */
  16.   background: linear-gradient(to bottom,  
  17.     #cdeb8e 0%,#b0ca34 100%); /* W3C standard, IE10+ */
  18.   filter: progid:DXImageTransform.Microsoft.gradient( 
  19.     startColorstr='#cdeb8e', 
  20.     endColorstr='#b0ca34',GradientType=0 ); /* IE6-9 */
  21.   }

This gradient output is directly from ColorZilla, and already includes prefixes and workarounds for older versions of IE. Note that for the unprefixed linear-gradient, it is recommended to precede the directional declaration with the word “to,” such as “to bottom” or “to top,” which is a change from just “top” or “bottom”.

Hover Styles

Pseudo class selectors allow you to target specific elements based on their position or state. For an interactive navigation bar, using pseudo class selectors (sometimes just called pseudo classes or pseudo selectors) grants more precise control of the appearance of the navigation item list. Pseudo classes are recognized by their preceding colon; for example, :hover or :active.

You might be familiar with the type of pseudo classes that target <anchor> elements based on their state.

  1. :visited
  2. :hover
  3. :active

In order to make the nav link appear visually different when the user hovers over it, append the :hover pseudo class to the a selector, and define a different background style.

  1. nav ul li a:hover {
  2.   background: #b0ca34; /* Old browsers */
  3.   background: -moz-linear-gradient(top,  
  4.     #b0ca34 0%, #96c40d 100%); /* FF3.6+ */
  5.   background: -webkit-gradient(linear, left top, left bottom, 
  6.     color-stop(0%,#b0ca34), 
  7.     color-stop(100%,#96c40d)); /* Chrome,Safari4+ */
  8.   background: -webkit-linear-gradient(top,  
  9.     #b0ca34 0%,#96c40d 100%); /* Chrome10+,Safari5.1+ */
  10.   background: -o-linear-gradient(top,  
  11.     #b0ca34 0%,#96c40d 100%); /* Opera 11.10+ */
  12.   background: linear-gradient(to bottom,  
  13.     #b0ca34 0%,#96c40d 100%); /* W3C standard, IE10+ */
  14.   filter: progid:DXImageTransform.Microsoft.gradient( 
  15.     startColorstr='#b0ca34', 
  16.     endColorstr='#96c40d',GradientType=0 ); /* IE6-9 */
  17.   }

Depending on the reset stylesheet you’re using (or not using!) you may need to override the default behavior of links in each of its dynamic pseudo class states. Typically, links change color when visited, and are underlined on hover.

  1. nav ul li a,
  2. nav ul li a:focus,
  3. nav ul li a:visited,
  4. nav ul li a:hover,
  5. nav ul li a:active {
  6.   color: #000;
  7.   text-decoration: none; }

Rounding Out the Corners

To round the corners on the first and last items in the nav list, you’ll need to use another type of pseudo class selector that note the position of an element within the document tree. Thesestructural pseudo classes show a relationship between an element and its parents or siblings. While not an exhaustive list, the most common structural pseudo classes are:

  1. :first-child
  2. :last-child
  3. :nth-of-type(n) 
    /* Where n is a number, targets the nth child. You can also use “even” or “odd” instead of a number. */

To round the corners, target the <a> in the first child and last child <li>.

  1. nav ul li:first-child a {
  2.   border-top-left-radius: 8px;
  3.   border-bottom-left-radius: 8px;
  4. }
  5. nav ul li:last-child a {
  6.   border-top-right-radius: 8px;
  7.   border-bottom-right-radius: 8px;
  8. }

Remember, there is only one anchor in each <li>, but many <li>s in the <ul>. Therefore, first target the <li> you want, and then the anchor within that element. A common mistake is to add the pseudo class to the anchor, and not the <li>. But since there’s only one anchor, it’s both the last child and the first child of its parent, and your styling will not work as planned. Moreover, the rounded corner will be applied on every single <a>.

Browser Compatibility

Depending on how important rounded corners are to you, prefixing might be necessary. A good resource for checking CSS browser support is, which will explain the level of support each browser type and version offers for a CSS property.

There is a known conflict between filter (which is what legacy IE browsers use for gradients) and border-radius. The filter does not listen to border-radius, so you’ll end up with square corners regardless of border-radius rules. Since IE10 supports background gradients (rather than a filter), I typically omit the filter rule from all gradient styles, and allow IE9 and lower to fall back to a simple background-color.

Personalize Your Design

Now that you have structure and styles for your navigation bar, take some time to personalize the color scheme, fonts and layout. There are some other fairly simple CSS properties that have big visual impact. text-shadow and box-shadow add dimension to your text and containing elements, and transition can apply a fade animation to the background-image without using any jQuery.

  1. box-shadow: 0 0 1px #000; /* x offset, y offset, size, color */
  2. text-shadow: 2px 2px 2px #000;
  3. transition: background 2s;

Similar to border-radius, not all browsers support these properties, so make sure to prefix appropriately.


With some basic HTML and CSS, you can create a simple navigation bar with a lot of visual impact.

  • Start with HTML to nail down a simple, semantic document structure
  • Focus on structural styles next. Tweak margins, paddings, and move your blokc elements into the position you want.
  • Next, it’s time to decorate. Take advantage of CSS3 gradients and border-radius. Refer to to know when to prefix, and employ the help of pseudo class selectors for interactive styles, or to target specific elements without needing an extra class name.

Full Code Example


  1. <nav>
  2.   <ul>
  3.     <li>
  4.         <a href="#">Home</a>
  5.     </li>
  6.     <li>
  7.         <a href="#">Print Design</a>
  8.     </li>
  9.     <li>
  10.         <a href="#">Web Design</a>
  11.     </li>
  12.     <li>
  13.         <a href="#">Bio</a>
  14.     </li>
  15.     <li>
  16.         <a href="#">Contact</a>
  17.     </li>
  18.   </ul>
  19. </nav>


  1. body {
  2.   width: 100%;
  3.   max-width: 960px;
  4.   margin: 0 auto; }
  5. nav {
  6.   width: 100%;
  7.   margin: 20px 0; }
  8. nav ul {
  9.   list-style: none;
  10.   overflow: hidden; }
  11. nav ul li {
  12.   float: left;
  13.   width: 20%; }
  14. nav ul li a {
  15.   text-align: center;
  16.   padding: 8px 0;
  17.   display: block;
  18.   width: 100%;
  19.   background: #cdeb8e; /* Old browsers */
  20.   background: -moz-linear-gradient(top,  
  21.     #cdeb8e 0%, #b0ca34 100%); /* FF3.6+ */
  22.   background: -webkit-gradient(linear, left top, left bottom, 
  23.     color-stop(0%,#cdeb8e), 
  24.     color-stop(100%,#b0ca34)); /* Chrome,Safari4+ */
  25.   background: -webkit-linear-gradient(top,  
  26.     #cdeb8e 0%,#b0ca34 100%); /* Chrome10+,Safari5.1+ */
  27.   background: -o-linear-gradient(top,  
  28.     #cdeb8e 0%,#b0ca34 100%); /* Opera 11.10+ */
  29.   background: linear-gradient(to bottom,  
  30.     #cdeb8e 0%,#b0ca34 100%); /* W3C, IE10+ */
  31.   filter: progid:DXImageTransform.Microsoft.gradient( 
  32.     startColorstr='#cdeb8e', 
  33.     endColorstr='#b0ca34',GradientType=0 ); /* IE6-9 */
  34.   }
  35. nav ul li a,
  36. nav ul li a:focus,
  37. nav ul li a:visited,
  38. nav ul li a:hover,
  39. nav ul li a:active {
  40.   color: #000;
  41.   text-decoration: none; }
  42. nav ul li a:hover,
  43. nav ul li a:active {
  44.   background: #b0ca34; /* Old browsers */
  45.   background: -moz-linear-gradient(top,  
  46.     #b0ca34 0%, #96c40d 100%); /* FF3.6+ */
  47.   background: -webkit-gradient(linear, left top, left bottom, 
  48.     color-stop(0%,#b0ca34), 
  49.     color-stop(100%,#96c40d)); /* Chrome,Safari4+ */
  50.   background: -webkit-linear-gradient(top,  
  51.     #b0ca34 0%,#96c40d 100%); /* Chrome10+,Safari5.1+ */
  52.   background: -o-linear-gradient(top,  
  53.     #b0ca34 0%,#96c40d 100%); /* Opera 11.10+ */
  54.   background: linear-gradient(to bottom,  
  55.     #b0ca34 0%,#96c40d 100%); /* W3C, IE10+ */
  56.   filter: progid:DXImageTransform.Microsoft.gradient( 
  57.     startColorstr='#b0ca34', 
  58.     endColorstr='#96c40d',GradientType=0 ); /* IE6-9 */
  59.   }
  60. nav ul li:first-child a {
  61.   border-top-left-radius: 8px;
  62.   border-bottom-left-radius: 8px; }
  63. nav ul li:last-child a {
  64.   border-top-right-radius: 8px;
  65.   border-bottom-right-radius: 8px; }

About the Author

Laura Athanasiou is a developer at HP Cloud. She’s passionate about semantic HTML, responsive design and sassy CSS. Follow her on Twitter at @rhein_wein.

The jungle is alive: Be it a collaboration between two or more authors or an article by an author not contributing regularly. In these cases you find the Noupe Editorial Team as the ones who made it. Guest authors get their own little bio boxes below the article, so watch out for these.

Tags: , , , , , ,

Sort by:   newest | oldest | most voted
2 years 8 months ago
“having sound markup structure is efficient and can eliminate the need for class names (for example, instead of , use ) and therefore keep your CSS files smaller and better performing.” I don’t agree with this. I do understand what you mean but I don’t think that’s how one could understand it just by reading this. If you write your styles based on element names, you can end up with very unefficient code, the kind that sets some default properties and then overrides them a hundred times… all thoses overrides add up to your file size and performance cost. In… Read more »
Chris Heilmann
2 years 8 months ago

Why is there no demo to see it all in action? I put it in a fiddle, here you go:

Dieter Petereit
Noupe Team
2 years 8 months ago

Thanks for the value added.

2 years 8 months ago

While the information is good, three things stuck out at me

1. the title of the post is “interactive navigation bar” but i was expecting more than just :hover – images, animation, drop downs
2. a link to a live example is always helpful
3. due to the large amount of mobile/responsive design, adding that to the css (@media) would have been good as well

2 years 5 months ago

Thank you !!!!!

2 years 2 months ago

How do you create a navigation bar that doesn’t reload everytime you press a link? I want just the body part to change. I’ve searched for hours without finding anything, given my extremely limited knowledge.

Michael Dixon
Michael Dixon
2 years 1 month ago
I have few different ideas: Idea #1: Load entire site into one page, all “non-active” parts of the site using display: none. When you click one of the links, use javascript to hide the current part of the body and to show the page you want to show. Idea #2: Use AJAX. When you click a button, a call to a server-side function is called that returns the body for the link’s corresponding page. that html is loaded into your body. Idea #3: Use a javascript template. By using javascript templates, you can create your html pages and then easily… Read more »