Unless you limit yourself to one-page web sites, you’ll need to design navigation. In fact, navigation is among the most important parts of any web design, and requires a great deal of thought if visitors are to move around your site easily.
Making site navigation easy is one area in which CSS really comes into its own. Older methods of creating navigation tended to rely on lots of images, nested tables, and JavaScript — all of which can seriously affect the usability and accessibility of a site. If your site cannot be navigated using a device that doesn’t support JavaScript, for example, not only are you blocking users who have turned JavaScript off, but you’re also locking out text-only devices such as screen readers and search engine robots — they’ll never get past your home page to index the content of your site. If your design clients don’t care about accessibility, tell them their clunky menu is stopping them from achieving a decent search engine ranking!
CSS allows you to create attractive navigation that, in reality, is no more than text — text that can be marked up in such a way as to ensure that it’s both accessible and understandable by all those who can’t physically see your design, but still want to get to your content. In this chapter, we’ll look at a variety of solutions for creating CSS-based navigation. Some are suited to implementation on an existing site, to make it load more quickly, and boost its accessibility by replacing an old-fashioned, image-based navigation. Others are more suited to incorporation within a pure CSS layout.
Before you begin, you might want to download this article in PDF format, so you can use it offline, at your leisure. The PDF also includes other chapters from The CSS Anthology: 101 Essential Tips, Tricks and Hacks on using CSS with images, and accessibility and alternative devices.
But now, on with the show.
How do I style a structural list as a navigation menu?
For new sites, you’re likely to be trying to avoid using tables for layout, or using them only where absolutely necessary. Therefore, a navigation solution that doesn’t involve tables is useful; also, by eradicating table elements, you’ll find that your page contains far less markup.
Solution
A navigation system is simply a list of places that users can visit on the site. Therefore, an unordered list is the ideal way to mark up your navigation. The navigation in Figure 1 is marked up as a list, and styled using CSS, as you can see.
Creating navigation by styling a list (click to view image)
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en-US”>
<head>
<title>Lists as navigation</title>
<meta http-equiv=”content-type”
content=”text/html; charset=utf-8″ />
<link rel=”stylesheet” type=”text/css” href=”listnav1.css” />
</head>
<body>
<div id=”navigation”>
<ul>
<li><a href=”#”>Recipes</a></li>
<li><a href=”#”>Contact Us</a></li>
<li><a href=”#”>Articles</a></li>
<li><a href=”#”>Buy Online</a></li>
</ul>
</div>
</body>
</html>
#navigation {
width: 200px;
}
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
}
#navigation li {
border-bottom: 1px solid #ED9F9F;
}
#navigation li a:link, #navigation li a:visited {
font-size: 90%;
display: block;
padding: 0.4em 0 0.4em 0.5em;
border-left: 12px solid #711515;
border-right: 1px solid #711515;
background-color: #B51032;
color: #FFFFFF;
text-decoration: none;
}
Discussion
To create navigation based on an unordered list, first create your list, placing each navigation link inside a li element:
<ul>
<li><a href="#">Recipes</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
Next, wrap the list in a div with an appropriate ID:
<div id="navigation">
<ul>
<li><a href="#">Recipes</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
</div>
As Figure 2 shows, this markup looks fairly ordinary with the browser’s default styles applied.
A very basic, unstyled list (click to view image)
The first thing we need to do is style the container in which the navigation sits — in this case, #navigation:
#navigation {
width: 200px;
}
I’ve given #navigation a width. If this navigation system were part of a CSS page layout, I’d probably add some positioning information to this ID as well.
Next, we style the list:
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
}
As Figure 3 illustrates, the above rule removes list bullets and the indented margin that browsers apply, by default, when displaying a list.
Viewing the list after indentation and bullets are removed (click to view image)
The next step is to style the li elements within #navigation, to give them a bottom border:
#navigation li {
border-bottom: 1px solid #ED9F9F;
}
Finally, we style the link itself:
#navigation li a:link, #navigation li a:visited {
font-size: 90%;
display: block;
padding: 0.4em 0 0.4em 0.5em;
border-left: 12px solid #711515;
border-right: 1px solid #711515;
background-color: #B51032;
color: #FFFFFF;
text-decoration: none;
}
Most of the work is done here, creating CSS rules to add left and right borders, remove the underline, and so on. The first property declaration in this rule sets the display property to block. This causes the link to display as a block element, meaning that the whole area of each navigation “button” is active when you move the cursor over it — the same effect you’d see if you used an image for the navigation.
Can I use CSS and lists to create a navigation system with subnavigation?
Sometimes, more than one navigation level is necessary — but is it possible to create multi-leveled navigation using styled lists in CSS?
Solution
The perfect way to display subnavigation within a navigation system is to create a sublist within a list. The two levels of navigation will be easy to understand when they’re marked up in this way — even in browsers that don’t support CSS.
To produce multi-level navigation, we create a nested list and style the colors, borders, and link properties of the new list’s items:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>Lists as navigation</title>
<meta http-equiv="content-type"
content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="listnav_sub.css" />
</head>
<body>
<div id="navigation">
<ul>
<li><a href="#">Recipes</a>
<ul>
<li><a href="#">Starters</a></li>
<li><a href="#">Main Courses</a></li>
<li><a href="#">Desserts</a></li>
</ul>
</li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
</div>
</body>
</html>
#navigation {
width: 200px;
}
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
}
#navigation li {
border-bottom: 1px solid #ED9F9F;
}
#navigation li a:link, #navigation li a:visited {
font-size: 90%;
display: block;
padding: 0.4em 0 0.4em 0.5em;
border-left: 12px solid #711515;
border-right: 1px solid #711515;
background-color: #B51032;
color: #FFFFFF;
text-decoration: none;
}
#navigation li a:hover {
background-color: #711515;
color: #FFFFFF;
}
#navigation ul ul {
margin-left: 12px;
}
#navigation ul ul li {
border-bottom: 1px solid #711515;
margin:0;
}
#navigation ul ul a:link, #navigation ul ul a:visited {
background-color: #ED9F9F;
color: #711515;
}
#navigation ul ul a:hover {
background-color: #711515;
color: #FFFFFF;
}
The result of these additions is shown in Figure 4.
The CSS list navigation containing subnavigation (click to view image)
Discussion
Nested lists are a perfect way to describe the navigation system that we’re working with here. The first list contains the main sections of the site; the sublist under Recipes shows the subsections within the Recipes category. Even without any CSS styling, the structure of the list is still clear and comprehensible, as you can see in Figure 5.
The navigation remaining logical without the CSS (click to view image)
The HTML that we use to mark up this list simply nests the sublist inside the li element of the appropriate main item:
<ul>
<li><a href="#">Recipes</a>
<ul>
<li><a href="#">Starters</a></li>
<li><a href="#">Main Courses</a></li>
<li><a href="#">Desserts</a></li>
</ul>
</li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
With this HTML, and without any changes to the CSS, the menu will display as shown in Figure 6, “The sublist taking on the styles of the main navigation”, where the li elements inherit the styles of the main menu.
The sublist taking on the styles of the main navigation (click to view image)
Let’s add a style rule for the nested list to communicate visually that it’s a submenu, and not part of the main navigation:
#navigation ul ul {
margin-left: 12px;
}
This rule will indent the nested list so that it’s in line with the right edge of the border for the main menu, as demonstrated in Figure 7.
The indented subnavigation (click to view image)
Let’s add some simple styles to the li and a elements within the nested list to complete the effect:
#navigation ul ul li {
border-bottom: 1px solid #711515;
margin: 0;
}
#navigation ul ul a:link, #navigation ul ul a:visited {
background-color: #ED9F9F;
color: #711515;
}
#navigation ul ul a:hover {
background-color: #711515;
color: #FFFFFF;
}
How do I make a horizontal menu using CSS and lists?
So far, we’ve dealt with vertical navigation — the kind of navigation that will most likely be found in a column to the left or right of a site’s main content area. However, site navigation is also commonly found as a horizontal menu close to the top of the document.
Solution
As Figure 8 shows, this type of menu can be created using styled lists in CSS. The li elements must be set to display inline so that each list item does not display on its own line.
Using CSS to create horizontal list navigation (click to view image)
Here’s the HTML and CSS that creates this display:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en-US”>
<head>
<title>Lists as navigation</title>
<meta http-equiv=”content-type”
content=”text/html; charset=utf-8″ />
<link rel=”stylesheet” type=”text/css” href=”listnav_horiz.css” />
</head>
<body>
<div id=”navigation”>
<ul>
<li><a href=”#”>Recipes</a></li>
<li><a href=”#”>Contact Us</a></li>
<li><a href=”#”>Articles</a></li>
<li><a href=”#”>Buy Online</a></li>
</ul>
</div>
</body>
</html>
#navigation {
font-size: 90%;
}
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
padding-top: 1em;
}
#navigation li {
display: inline;
}
#navigation a:link, #navigation a:visited {
padding: 0.4em 1em 0.4em 1em;
color: #FFFFFF;
background-color: #B51032;
text-decoration: none;
border: 1px solid #711515;
}
#navigation a:hover {
color: #FFFFFF;
background-color: #711515;
}
Discussion
To create the horizontal navigation, we start with a list that’s identical to the one we created for our vertical list menu:
<div id="navigation">
<ul>
<li><a href="#">Recipes</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
</div>
We style the #navigation container to apply some basic font information, as we did with the vertical navigation. In a CSS layout, this ID would probably also contain some additional styles that determine the navigation’s position on the page:
#navigation {
font-size: 90%;
}
In styling the ul element, we remove the list bullets and default indentation applied to the list by the browser:
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
padding-top: 1em;
}
The property that transforms our list from a vertical to a horizontal display is applied to the li element:
#navigation li {
display: inline;
}
After we set the display property to inline, the list looks like Figure 9.
Displaying the list menu horizontally (click to view image)
All that’s left for us to do is to style the links for our navigation:
#navigation a:link, #navigation a:visited {
padding: 0.4em 1em 0.4em 1em;
color: #FFFFFF;
background-color: #B51032;
text-decoration: none;
border: 1px solid #711515;
}
#navigation a:hover {
color: #FFFFFF;
background-color: #711515;
}
If you’re creating boxes around each link, as I have here, remember that, in order to make more space between the text and the edge of its container, you’ll need to add more left and right padding to the links. To create more space between the navigation items, add left and right margins to the links.
How do I create button-like navigation using CSS?
Navigation that appears to be composed of clickable buttons is a feature of many web sites. This kind of navigation is often created using images to which effects are applied to make the edges look beveled and button-like. Often, some JavaScript code is used to swap in another image, so the button appears to depress when the user holds the cursor over it or clicks on the image.
Is it possible to create such button-like navigation systems using only CSS? Absolutely!
Solution
Creating a button effect like that shown in Figure 10 is possible, and fairly straightforward, using CSS. The effect’s success hinges on your use of the CSS border properties.
Building button-like navigation with CSS (click to view image)
Here’s the code you’ll need:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>Lists as navigation</title>
<meta http-equiv="content-type"
content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="listnav_button.css"
/>
</head>
<body>
<div id="navigation">
<ul>
<li><a href="#">Recipes</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
</div>
</body>
</html>
#navigation {
font-size:90%
}
#navigation ul {
list-style: none;
margin: 0;
padding: 0;
padding-top: 1em;
}
#navigation li {
display: inline;
}
#navigation a:link, #navigation a:visited {
margin-right: 0.2em;
padding: 0.2em 0.6em 0.2em 0.6em;
color: #A62020;
background-color: #FCE6EA;
text-decoration: none;
border-top: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-bottom: 1px solid #717171;
border-right: 1px solid #717171;
}
#navigation a:hover {
border-top: 1px solid #717171;
border-left: 1px solid #717171;
border-bottom: 1px solid #FFFFFF;
border-right: 1px solid #FFFFFF;
}
Discussion
To create this effect, we’ll use the horizontal list navigation described in the section called “How do I make a horizontal menu using CSS and lists?” However, to create the button look, we’ll use different colored borders at the top and left than we use for the bottom and right sides of each button. By giving the top and left edges of the button a lighter colored border than we assign to the button’s bottom and right edges, we create a slightly beveled effect:
#navigation a:link, #navigation a:visited {
margin-right: 0.2em;
padding: 0.2em 0.6em 0.2em 0.6em;
color: #A62020;
background-color: #FCE6EA;
text-decoration: none;
border-top: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-bottom: 1px solid #717171;
border-right: 1px solid #717171;
}
We reverse the border colors for the hover state, which creates the effect of the button being pressed:
#navigation a:hover {
border-top: 1px solid #717171;
border-left: 1px solid #717171;
border-bottom: 1px solid #FFFFFF;
border-right: 1px solid #FFFFFF;
}
Try using heavier borders, and changing the background images on the links, to create effects that suit your design.
How do I create tabbed navigation with CSS?
Navigation that appears as tabs across the top of the page is a popular navigation choice. Many sites create tabs using images. However, this approach suffers from the problems associated with text contained in images, which we discussed in the section called “How do I replace image-based navigation with CSS?” However, it is possible to create a tab effect by combining background images and text styled with CSS.
Solution
The tabbed navigation shown in Figure 11 can be created by styling a horizontal list.
Using CSS to create tabbed navigation (click to view image)
Here’s the HTML and CSS that creates this effect:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en-US”>
<head>
<title>Lists as navigation</title>
<meta http-equiv=”content-type”
content=”text/html; charset=utf-8″ />
<link rel=”stylesheet” type=”text/css” href=”tabs.css” />
</head>
<body id=”recipes”>
<div id=”header”>
<ul id=”tabnav”>
<li class=”recipes”><a href=”#”>Recipes</a></li>
<li class=”contact”><a href=”#”>Contact Us</a></li>
<li class=”articles”><a href=”#”>Articles</a></li>
<li class=”buy”><a href=”#”>Buy Online</a></li>
</ul>
</div>
<div id=”content”>
<h1>Recipes</h1>
<p>Lorem ipsum dolor sit amet, … </p>
</div>
</body>
</html>
body {
font: .8em/1.8em verdana, arial, sans-serif;
background-color: #FFFFFF;
color: #000000;
margin: 0 10% 0 10%;
}
#header {
float: left;
width: 100%;
border-bottom: 1px solid #8DA5FF;
margin-bottom: 2em;
}
#header ul {
margin: 0;
padding: 2em 0 0 0;
list-style: none;
}
#header li {
float: left;
background-image: url("images/tab_left.gif“);
background-repeat: no-repeat;
margin: 0 1px 0 0;
padding: 0 0 0 8px;
}
#header a {
float: left;
display: block;
background-image: url("images/tab_right.gif");
background-repeat: no-repeat;
background-position: right top;
padding: 0.2em 10px 0.2em 0;
text-decoration: none;
font-weight: bold;
color: #333366;
}
#recipes #header li.recipes,
#contact #header li.contact,
#articles #header li.articles,
#buy #header li.buy {
background-image: url("images/tab_active_left.gif");
}
#recipes #header li.recipes a,
#contact #header li.contact a,
#articles #header li.articles a,
#buy #header li.buy a {
background-image: url("images/tab_active_right.gif");
background-color: transparent;
color:#FFFFFF;
}
Discussion
The tabbed navigation approach I’ve used here is a basic version of Douglas Bowman’s Sliding Doors of CSS method, which is a tried and tested technique for creating a tabbed interface. The structure that I’ve given to the navigation menu is the same kind of simple unordered list that we’ve worked with so far, except that each list item is assigned a class attribute that describes the link it contains. We’ve also wrapped the entire list in a div with an id of header. The technique takes its name from the two images used to implement it — one overlaps the other, and the images slide apart as the text size increases.
You’ll need four images to create this effect: two to create the regular tab color, and two to use when the tab is the currently selected (highlighted) tab. The images I’ve used in this example are shown in Figure 12. As you can see, they’re far wider and taller than would generally be necessary for a tab?this provides plenty of space for the tab to “grow” if the user’s browser is configured to display text at a very large size.
The image files used to create the tabs (click to view image)
Here’s the basic list of navigation items:
<div id="header">
<ul id="tabnav">
<li class="recipes"><a href="#">Recipes</a></li>
<li class="contact"><a href="#">Contact Us</a></li>
<li class="articles"><a href="#">Articles</a></li>
<li class="buy"><a href="#">Buy Online</a></li>
</ul>
</div>
The first step is to style the container that surrounds the navigation. We’re going to give our header a simple bottom border for the purposes of this exercise, but on a real-world site this container may hold other elements in addition to our tabs (such as a logo or search field):
#header {
float: left;
width: 100%;
border-bottom: 1px solid #8DA5FF;
margin-bottom: 2em;
}
As you’ll have noticed, we float the header to the left. We’ll also float the individual list items; floating the container that houses them ensures that they remain contained once they’re floated, and that the border will display below them.
Next, we create a style rule for the ul element inside the header:
#header ul {
margin: 0;
padding: 2em 0 0 0;
list-style: none;
}
This rule removes the bullets and alters the margin and padding on our list — we’ve added two ems of padding to the top of the ul element. Figure 13 shows the results of our work so far.
Displaying the navigation after styling the /#c#/ul/#ec#/ element (click to view image)
Now we need to style the list items:
#header li {
float: left;
background-image: url("images/tab_left.gif");
background-repeat: no-repeat;
margin: 0 1px 0 0;
padding: 0 0 0 8px;
}
This rule uses the float property to position the list items horizontally while maintaining the block-level status of each. We then add the first of our “sliding door” images — the thin left-hand side of the tab — as a background image. A single-pixel right margin on the list item creates a gap between one tab and the next. Figure 14 shows that the left-hand tab image now appears for each tab.
The navigation tabs reflecting the new styles (click to view image)
Next, we style the links, completing the look of our tabs in their unselected state. The image that forms the right-hand side of the tab is applied to each link, completing the tab effect:
#header a {
float: left;
display: block;
background-image: url("images/tab_right.gif");
background-repeat: no-repeat;
background-position: right top;
padding: 0.2em 10px 0.2em 0;
text-decoration: none;
font-weight: bold;
color: #333366;
}
The results are shown in Figure 15.
Styling the navigation links (click to view image)
If you increase the text size in the browser, you can see that the tabs neatly increase in size too. In fact, they do so without overlapping and without the text protruding out of the tab — this is because we have used images that allow plenty of room for growth.
To complete the tab navigation, we need to highlight the tab that corresponds to the currently displayed page. You’ll recall that each list item has been assigned a unique class name. If we assign to the body element an ID that has a value equal to the value of each list item class, CSS can do the rest of the work:
<body id="recipes">
Although it looks like a lot of code, the CSS code that styles the tab matching the body ID is relatively straightforward. The images I’ve used are exact copies of the left and right images that we applied to the tabs, but they’re a different color, which produces the effect of one tab appearing to be highlighted.
Here’s the CSS:
#recipes #header li.recipes,
#contact #header li.contact,
#articles #header li.articles,
#buy #header li.buy {
background-image: url("images/tab_active_left.gif");
}
#recipes #header li.recipes a,
#contact #header li.contact a,
#articles #header li.articles a,
#buy #header li.buy a {
background-image: url("images/tab_active_right.gif");
background-color: transparent;
color: #FFFFFF;
}
With these rules in place, specifying an ID of recipes to our body will cause the Recipes tab to be highlighted, specifying contact will cause the Contact Us tab to be highlighted, and so on. The results of this work are shown in Figure 16.
Identifying a Useful Technique
The technique of adding an ID to the body element can be very useful. For example, you may have different color schemes for different sections of your site, to help the user identify which section they’re using. You can simply add the section name to the body element and make use of it within the style sheet, as we did in this example.
Highlighting the Contact Us tab (click to view image)
How do I create rollovers in CSS without using JavaScript?
CSS-based navigation can provide some really interesting effects, but there are still some effects that require the use of images. Is it possible to enjoy the advantages of text-based navigation and still use images?
Solution
It is possible to combine images and CSS to create JavaScript-free rollovers. This solution is based on a technique described at WellStyled.com. Here’s the code you’ll need:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en-US”>
<head>
<title>Lists as navigation</title>
<meta http-equiv=”content-type”
content=”text/html; charset=utf-8″ />
<link rel=”stylesheet” type=”text/css” href=”images.css” />
</head>
<body>
<ul id=”nav”>
<li><a href=”#”>Recipes</a></li>
<li><a href=”#”>Contact Us</a></li>
<li><a href=”#”>Articles</a></li>
<li><a href=”#”>Buy Online</a></li>
</ul>
</body>
</html>
ul#nav {
list-style-type: none;
padding: 0;
margin: 0;
}
#nav a:link, #nav a:visited {
display: block;
width: 150px;
padding: 10px 0 16px 32px;
font: bold 80% Arial, Helvetica, sans-serif;
color: #FF9900;
background: url("peppers.gif“) top left no-repeat;
text-decoration: none;
}
#nav a:hover {
background-position: 0 -69px;
color: #B51032;
}
#nav a:active {
background-position: 0 -138px;
color: #006E01;
}
The results can be seen in Figure 17, but to enjoy the full effect I suggest you try it for yourself. Don’t forget to click on a link or two!
Using images to advantage in the completed menu (click to view image)
Discussion
This solution offers a means of using images in your navigation without having to resort to preloading lots of separate files.
The navigation has three states, but these states aren’t depicted using three separate images. Instead, we use one large image that contains images for all three states, as shown in Figure 18.
The pepper image containing images for all three rollover states (click to view image)
The navigation is marked up as a simple list:
<ul id="nav">
<li><a href="#">Recipes</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Articles</a></li>
<li><a href="#">Buy Online</a></li>
</ul>
We control the display of the background image within the declaration block for the navigation links. However, because the image is far bigger than the area required for this element, we only see the yellow pepper at first:
#nav a:link, #nav a:visited {
display: block;
width: 150px;
padding: 10px 0 16px 32px;
font: bold 80% Arial, Helvetica, sans-serif;
color: #FF9900;
background: url("peppers.gif") top left no-repeat;
text-decoration: none;
}
When the :hover state is activated, the background image moves up the exact number of pixels required to reveal the red pepper. In this example, I had to move it by 69 pixels, but this figure will vary depending on the image that you use. You could probably work it out mathematically, or you could do as I do and simply increment the background position a few pixels at a time, until it appears in the right location on hover:
#nav a:hover {
background-position: 0 -69px;
color: #B51032;
}
When the :active state is activated, the background image shifts again, this time to display the green pepper when the link is clicked:
#nav a:active {
background-position: 0 -138px;
color: #006E01;
}
That’s all there is to it! The effect can fall apart if the user resizes the text in the browser to a larger font, which allows the edges of the hidden images to display. You can anticipate this eventuality to some degree by leaving quite a large space between each of the three images — keep this in mind when preparing your images.
Image Flickering in Internet Explorer
This technique sometimes causes the navigation to “flicker” in Internet Explorer. In my tests, this only tends to be a problem when the image is larger than the ones we’ve used here; however, if your navigation items flicker, a well-documented remedy is available.
How can I create pure CSS drop-down menus?
In the previous section, we learned to create image- and JavaScript-free rollovers. Can the same be achieved for drop-down menus?
Solution
The answer is yes … but the resulting menus don’t work in Internet Explorer 6! Nevertheless, Figure 19 illustrates this interesting technique, which will become more useful as Internet Explorer 7 gains market share.
Creating a CSS-only drop-down menu (click to view image)
Here’s the markup used for this example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>CSS Flyout menus</title>
<meta http-equiv="content-type"
content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="menus.css" />
</head>
<body>
<ul id="nav">
<li><a href="#">Starters</a>
<ul>
<li><a href="">Fish</a></li>
<li><a href="">Fruit</a></li>
<li><a href="">Soups</a></li>
</ul>
</li>
<li><a href="#">Main courses</a>
<ul>
<li><a href="">Meat</a></li>
<li><a href="">Fish</a></li>
<li><a href="">Vegetarian</a></li>
</ul>
</li>
<li><a href="#">Desserts</a>
<ul>
<li><a href="">Fruit</a></li>
<li><a href="">Puddings</a></li>
<li><a href="">Ice Creams</a></li>
</ul>
</li>
</ul>
</body>
</html>
And here are the style rules to implement this effect:
body {
font: 1em Verdana, Arial, sans-serif;
background-color: #FFFFFF;
color: #000000;
margin: 1em 0 0 1em;
}
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
}
#nav li {
float: left;
position: relative;
width: 10em;
border: 1px solid #B0C4DE;
background-color: #E7EDF5;
color: #2D486C;
font-size: 80%;
margin-right: 1em;
}
#nav a:link, #nav a:visited {
display: block;
text-decoration: none;
padding-left: 1em;
color: #2D486C;
}
* html #nav a {
width: 100%;
}
#nav ul {
display: none;
position: absolute;
padding: 0;
}
#nav ul li {
border: 0 none transparent;
border-bottom: 1px solid #E7EDF5;
border-top: .5em solid #FFF;
background-color: #F1F5F9;
font-size: 100%;
margin-bottom: -1px;
margin-top: 1px;
padding: 0;
}
#nav li:hover ul {
display: block;
}
Discussion
Though this attractive and easy effect will not work in Internet Explorer 6, it is supported by several other, newer browsers. This solution allows you to create a drop-down menu without using any JavaScript at all. The technique is based on the Suckerfish Dropdowns solution detailed on A List Apart.
The menus themselves are based on simple unordered lists. The top-level menu items consist of one main list; the items that fall under each main item are contained in nested lists:
<ul id="nav">
<li><a href="#">Starters</a>
<ul>
<li><a href="">Fish</a></li>
<li><a href="">Fruit</a></li>
<li><a href="">Soups</a></li>
</ul>
</li>
⋮
As you can see in Figure 20, when styles aren’t applied to the menu, the page displays as a logically structured, unordered list with subsections that are easy to spot.
Displaying lists logically in browsers that don’t support CSS (click to view image)
To begin with, we style the top-level menu, removing its list style. We also float the list items to the left so that they stack horizontally. The list items are given a position value of relative so that we can position our fly-out menus within them later on:
#nav, #nav ul {
⋮
list-style: none;
}
#nav li {
float: left;
position: relative;
width: 10em;
⋮
margin-right: 1em;
}
We coerce the links in the menu to display as blocks, so they fill the rectangular areas defined by the menu items. Internet Explorer 6 (and earlier) doesn’t recognize this; however, setting the width of each link to 100% ensures that our clickable region expands to fill the containing block.
#nav a:link, #nav a:visited {
display: block;
⋮
}
* html #nav a {
width: 100%;
}
Next, we style the nested lists that constitute our fly-out menus so that, by default, they are not displayed (display: none). We do, however, specify that absolute positioning is to be used when they are displayed, so that they don’t affect the flow of the rest of the document:
#nav ul {
display: none;
position: absolute;
⋮
}
To prevent our fly-out menu list items from being floated horizontally the way the main menu items are, we need to set their float property to none:
#nav ul li {
float: none;
⋮
}
Finally, we use the :hover pseudo-class to display the fly-out menu within any main menu item when the cursor is moved over it:
#nav li:hover ul {
display: block;
}
With these basic CSS rules in place, the menus display as shown in Figure 21.
Altering the menu display with the addition of basic CSS (click to view image)
This code initially sets the nested lists to display: none. When the user hovers the cursor over a main menu list item, the property of the nested list within that list item is set to display: block, and the menu appears. However, this approach doesn’t work in Internet Explorer, as in that browser the :hover pseudo-class works only on links — not on any other element.
The rest of the CSS simply applies visual styles to make the menus look good.
Falling Between the Cracks
When a fly-out menu opens, the user must move the cursor down to the fly-out menu items to select one. If, in this motion, the cursor moves outside of the list item that opened the fly-out menu, the menu will close immediately, as the :hover pseudo-class will no longer be in effect.
Looking at the style rules for this page, you can see that we use absolute positioning to display the nested list over the top of the rest of the page content without disturbing it.
In theory, we should be able to leave a little space between the top-level menu item and the fly-out menu simply by adding margin to the top of the list; however, in Internet Explorer 7 the fly-out menu will disappear if the cursor passes over a margin area, rendering the menu unusable. Instead, I’ve created the effect by applying a white border to the top of the menu.
I’ve also added a very small margin to the top of each list item, and a negative margin of the same amount to the bottom. This has the effect of shifting our menu down by one pixel — just enough to ensure that our white border doesn’t cover up the bottom of our top-level menu item.
#nav ul li {
border: 0 none transparent;
border-bottom: 1px solid #E7EDF5;
border-top: .5em solid #FFF;
background-color: #F1F5F9;
font-size: 100%;
margin-bottom: -1px;
margin-top: 1px;
padding: 0;
}
Accessibility Concerns
When you’re using any drop-down menu — with or without JavaScript — make sure that users who don’t see the full effect of the menus are still able to move around your site.
In the case of this example, users who don’t have CSS support will see the expanded nested lists, and will be able to navigate through the site. Anyone who uses a browser that doesn’t support the display of the submenus, such as Internet Explorer 6, will still be able to navigate so long as the pages to which the top-level menu items link contain links to all the pages in that section’s submenu.
Any menu system that prevents users whose browsers don’t support it from navigating the site is bad news.
Summary
This chapter has discussed a range of different ways in which we can create navigation using structurally sound markup, and provided examples that can be used as starting points for your own experiments.
On existing sites where a full redesign is not possible, introducing a CSS-based navigation system can be a good way to improve the site’s accessibility and load speed without affecting its look and feel in a big way.
Where to next? Download this article in PDF format, and you’ll also receive two other chapters:
- Chapter 3, CSS and Images, which demonstrates the basics of working with images and answers common image-related questions
- Chapter 8, Accessibility and Alternative Devices, which moves through the basics of separating content from presentation before dealing with questions of alternative style sheets and addressing users’ particular accessibility needs
And don’t forget to check out the Table of Contents for more on what’s in The CSS Anthology: 101 Essential Tips, Tricks and Hacks.