• Create an Advanced CSS Menu Using the Hover and Position Properties

  • By: Jarod Taylor | Category: HTML & CSS
  • Starting with a photoshop file and finishing with semantic HTML and CSS, we'll be creating an advanced CSS navigation menu using its :hover and position properties.

    The finished product will render properly in all major browsers including Firefox, Safari, IE7/IE8, Opera, and Google Chrome. Due to IE6 and its lack of support for the :hover property (other than on anchor elements), we'll have to implement a little javascript to gain its support. Nonetheless, let's get started!

    Get SourceView Demo

    Introduction

    tutorial image

    This tutorial uses techniques that were also covered in last week's tutorial, CSS Background Image Sprites: A Beginner’s Guide . So, if you need to brush up on what CSS sprites are and how they're used, I would recommend doing so before continuing this tutorial.

    Before we get started, I want to give credit to Chris Spooner for his Hand Drawn Doodle Icon Set we'll be using in this tutorial. We're also using the Sketch Rockwell (Block) font, which can be downloaded here.

    Steps:
    1. Get images ready for our menu
    2. Mark up the HTML
    3. Add the CSS
    4. Write some jQuery to get IE6 to behave

    We're going to start by slicing our PSD file into usable images for our menu. There are obviously more ways than one to prepare images for the web, but I'll be showing you how I do it on a regular basis. If you have your own way of doing things, by all means do so. Without further ado, let's get started.


    Getting The Images Ready

    I'm using Photoshop CS4 in this tutorial, but you can pretty much use any version of photoshop to achieve the same results. Go ahead, fire it up, and open the menu layout.psd included in the source files.

    1. Navigation Menu Background

    Turn off all layers except for the background group layer. Go to File > Save for Web & Devices, choose the JPEG High preset, and save it as nav-bg.jpg to your images folder.

    tutorial image
    2. Navigation Menu Buttons

    Turn on the home group layer. Make sure all 4 layers inside the group are visible. With the rectangle marquee selection tool, make a selection around the Home text, and the dashed underline -- be sure to include all the scribble in your selection. It sometimes makes it easier to zoom in when making the selection. Your selection should be 118 x 37px.

    tutorial image

    Go to Edit > Copy Merged (Do not deselect your marquee selection). Go to File > New, and make sure it has a width of 118px and a height of 37px. Don't paste anything yet. Go back to your main layout.psd and hide all layers except for the Home text. With the same marquee selection still selected go to Edit > Copy Merged again and then paste it into your newly created file.

    tutorial image

    Some of you may be wondering why we Copy Merged twice, so I'm going to explain it real quick. If you had just Copy Merged the text only, the File > New would've been only 110 x 27px because Photoshop will only Copy Merge visible layers. We want our menu button states to all be the same size.

    Each menu item has 3 rollover states. One is just plain text, one has a dashed underline, and one has a scribble over it. All three of them will be the same size; in this case the Home button is 118 x 37px. So, we need our Home button image to be 118 x 111px (37 x 3 = 111). In your file (with the newly pasted Home text) go to Image > Canvas Size, make sure the Relative box is unchecked, choose the center up arrow in the anchor section, change the height from 37px to 111px in the height input field, and hit OK. Go ahead and either delete the background layer, or at least hide it.

    tutorial image

    Go to View > New Guide, select Horizontal, input 37px in the position field and hit okay. Do it one more time, but this time input 74px in the position field. You should have something like this:

    tutorial image

    Now, go back to your main layout.psd (you should still have your marquee selection), activate the dashed underline layer (you should now see just the Home text and the dashed underline), Edit > Copy Merged, and paste it into your Home button image. Using your guides align it to the top (37px) guide. Go back to your main layout.psd, hide the dashed underline layer, and activate your scribble layer. You should now see the Home text and the scribble. Edit > Copy Merged and paste it into your Home button image. Align this one to the second guide (74px).

    tutorial image

    Now return back to your main layout.psd file and activate your background group layer. With the eyedropper tool, sample a common color surrounding your Home button.

    tutorial image

    The color you sampled should now be your foreground color. Click on your foreground color to bring up your color Color Picker. Copy the 6 digit hexadecimal color code by highlighting it, right clicking, and choosing copy (or ctrl-C/cmd-C).

    tutorial image

    Now return to your Home button, choose File > Save for Web & Devices, select either PNG-8 or GIF preset, make sure the transparency box is checked, click on matte and paste the color code into the hexadecimal color code field. Save as nav-home.png in your images folder.

    tutorial image

    Repeat these steps for the other 3 buttons (About, Blog, and Contact) and save them as nav-about.png, nav-blog.png, and nav-contact.png in your images folder.

    Keep in mind the following:

    • All the buttons have different widths, but have the same height (37px) so each button file will have a height of 111px.
    • Don't forget to sample a color (with the eyedropper tool) from each of the individual menu button's surrounding areas. The colors differ due to the multi-textured and colored background.

    You should now have 4 button images all having equal heights (111px), transparent backgrounds, and saved with a matte color matching a sample of its surrounding background.

    tutorial image
    3. Navigation Menu Icons

    In your main layout.psd file, activate each of the icon layers. Make a selection around your home icon with the rectangle marquee selection tool. With the eyedropper tool, sample a common color surrounding the icon. You're going to need to use that color (for the matte) when saving the icon(s), so go ahead and open your foreground color picker and copy the hexadecimal color code. Deactivate all layers except for the home icon you're working with (if you hold alt/option and click on the eye to the left of the layer you're working on, it will deactivate all other layers). Edit > Copy Merged and paste into a new file. In the new file, you don't need the white background, so go ahead and delete it. Go to File > Save for Web & Devices, make sure you change the matte color to the color you sampled earlier, and save the file as nav-home-icon.png in your images folder.

    Do the same thing with the 3 other icons. This is what I ended up with:

    tutorial image

    That's it! We should have a total of 9 images in our images folder now.

    1. nav-bg.jpg
    2. nav-home.png
    3. nav-home-icon.png
    4. nav-about.png
    5. nav-about-icon.png
    6. nav-blog.png
    7. nav-blog-icon.png
    8. nav-contact.png
    9. nav-contact-icon.png

    We now have all the images we need for our menu, let's get the html marked up next.

    HTML Markup

    Before we get started, let me just let you know how I have my file structure for our menu. In my main root folder I have 3 folders (/images, /js, and /css) and the index.html file. You are more than welcome to use whatever structure you're used to, however I'll be assuming your structure is the same as mine in this tutorial.

    Using your text editor of choice, start a new html document, and save it as index.html in your main directory. Here's our html for the navigation menu.

    <div id="nav-container">
        <ul id="nav">
            <li><a href="#" class="home">Home<span></span></a></li>
            <li><a href="#" class="about">About<span></span></a></li>
            <li><a href="#" class="blog">Blog<span></span></a></li>
            <li><a href="#" class="contact">Contact<span></span></a></li>
        </ul>
    </div>
    

    We have a div containing not only our unordered list, but it will also house our background image. We have an unordered list with an id="nav" so we can target it in our CSS. We've also assigned classes to each of our anchor elements within our list items. You'll also notice there's an empty span tag in each of the anchor elements. These are going to be the homes for our menu icons.

    Like I said, nothing out of the ordinary here; pretty simple stuff. Let's go ahead and write the CSS magic next.

    Add Some CSS

    First off, let's create a blank file and save it as nav-styles.css in the /css directory. We're going to do the CSS piece by piece so that it's easily digestible. First we'll start off with the container and then move on to the menu items.

    #nav-container
    #nav-container {
    	background: url(../images/nav-bg.jpg) no-repeat 0 0;
    	height: 178px;
    	width: 715px;
    }
    

    Our container's main purpose is to hold our main background image. When you apply the techniques you learn in this tutorial to your other projects, you'll most likely not need this container.

    ul#nav
    ul#nav {
    	list-style: none;
    	margin: 0;
    	padding: 0;
    	position: relative;
    }
    

    We're setting the unordered list to list-style: none to remove the bullets. We're also zeroing out the default margins and padding. Declaring our position property with a value of relative will allow for us to strategically place our menu buttons using absolute positioning. This part is important; if you're not familiar with how relative and absolute positioning works, I recommend reading through Chris Coyier's tutorial on CSS-Tricks.

    ul#nav li a
    ul#nav li a {
    	background-position: left top;
    	background-repeat: no-repeat;
    	display: block;
    	text-indent: -9000px;
    	position: absolute;
    }
    

    We're targeting all anchor elements in our unordered list. We are doing this for many reasons. All of our menu items share common style declarations. Rather than repeating ourself on each anchor element's style properties, we'll be able to simply target all the anchors and declare the common styles one time.

    First two things we did were background declarations. By setting our anchor background-position values to Left Top, all of our menu buttons will display the way we want (just the menu text, no dashed-underline or scribble).

    We declared our display property as block. Anchor elements, by default are display: inline, which will not allow us to declare widths on our menu buttons. By setting our display to block, we'll be able to give each of our anchor elements (menu buttons) width and height declarations. If you want to learn more about the CSS display properties inline and block, take a look at this article.

    Next, we declared our anchors' text-indent property values as -9000px. This allows for our menu items to still contain the proper text values they need (Home, About, Blog, Contact) without sitting right on top of our menu button images.

    Lastly, as I stated in the previous step (ul#nav), by declaring our anchors position property as absolute - we're able to strategically place our menu items by using the top, right, bottom, and left properties.

    ul#nav li a:hover
    ul#nav li a:hover{
    	background-position: left center;
    }
    

    When someone hovers over one of our menu items, the anchor element's background position will shift its y position to center (the dashed underline). This technique is what's referred to as a sprite. For more information on CSS sprites, please take a look at last week's tutorial CSS Background Image Sprites: A Beginner’s Guide.

    tutorial image
    ul#nav li a span
    ul#nav li a span{
    	background-repeat: no-repeat;
    	display: none;
    	position: absolute;
    }
    

    As I had said earlier, our span tags are going to be where our menu icons live. Like our anchor elements, the span elements will also share some common styles. There's no need for our icons to repeat, so we of course set our background-repeat property to no-repeat. Like our anchor elements, we want to position our icons where we please, so we set its position to absolute. We've declared our display property value as none. This way it won't show to the end user until its parent element (anchor) is hovered.

    ul#nav li a:hover span
    ul#nav li a:hover span{
    	display: block;
    }
    

    When the end user hovers one of our menu items, the contained span element will go from display: none to display: block.

    a.home
    ul#nav li a.home {
    	background-image: url(../images/nav-home.png);
    	height: 37px;
    	width: 118px;	
    	top: 100px;
    	left: 80px;	
    }
    

    Because we declared some default background values in the ul#nav li a style declarations, we simply need to give our background-image its url to the image.

    The nav-home.png's size is actually 118 x 111px, but because we only want to show part of it at any given time, we declared the height as 37px.

    Because we set all anchor position values to absolute we're able to move them where ever we want by using the top and left properties.

    a.home span
    ul#nav li a.home span{
    	background-image: url(../images/nav-home-icon.png);
    	width: 38px;
    	height: 33px;
    	top: -40px;
    	left: 40px;
    }
    

    Much like its container (anchor) we've declared its background image url as well as its respected height and width.

    Now, because, our span elements are also absolutely positioned, we're able to use the top and left properties and move our span tags where ever we want. I've positioned my icon just above the menu text, but you can really position it where ever you please!

    a.about & a.about span
    ul#nav a.about{
    	background-image: url(../images/nav-about.png);
    	height: 37px;
    	width: 109px;
    	top: 100px;
    	left: 230px;
    }
    
    ul#nav li a.about span{
    	background-image: url(../images/nav-about-icon.png);
    	width: 43px;
    	height: 27px;
    	top: -40px;
    	left: 40px;
    }
    
    a.blog & a.blog span
    ul#nav a.blog{
    	background-image: url(../images/nav-blog.png);
    	height: 37px;
    	width: 86px;
    	top: 100px;
    	left: 380px;
    }
    
    ul#nav li a.blog span{
    	background-image: url(../images/nav-blog-icon.png);
    	width: 45px;
    	height: 31px;
    	top: -40px;
    	left: 20px;
    }
    
    a.contact & a.contact span
    ul#nav a.contact{
    	background-image: url(../images/nav-contact.png);
    	height: 37px;
    	width: 141px;
    	top: 100px;
    	left: 505px;
    }
    
    ul#nav li a.contact span{
    	background-image: url(../images/nav-contact-icon.png);
    	width: 42px;
    	height: 29px;
    	top: -40px;
    	left: 49px;
    }
    

    All of our menu styles have similar declarations with different values. Obviously each menu button has the same height (37px) but different widths. The same goes for the span elements.

    If you take a look at what we have so far, we already have a pretty slick menu going. Now we need to add some additional styles to get our menu item background positions to shift to left bottom when they're supposed to.

    ul#nav:hover li a

    Modern browsers support :hover on not only anchor elements but just about any other element in an HTML document. What we need to do to get our menu items to shift its background position from left top to left bottom and display our scribbled out menu text is apply :hover to an element that is a parent element to all our anchors.

    tutorial image

    If we apply a hover attribute to our ul#nav element, we can then shift the background position of all its containing anchors to left bottom by targeting its children li a elements.

    Paste (or type) this at the bottom of your style sheet.

    ul#nav:hover li a{
    	background-position: left bottom;	
    }
    

    If you take a look at our menu now you'll notice something's wrong. When you hover over a link, all of your anchor element backgrounds shift to left bottom. That's not what we want! When our ul#nav is hovered, we want all menu anchor elements to shift left bottom to display our scribbled out button except for the anchor we're actually hovering.

    In order to achieve what we're wanting to achieve, you need to make sure you put your ul#nav:hover li a styles above your ul#nav li a:hover styles.

    tutorial image

    What happened when you had the ul#nav:hover styles at the bottom of your stylesheet is simple CSS hierarchy and inheritance. In a nutshell, the style declarations were overriding the styles for ul#nav li a:hover. By placing the styles above the ul#nav li a:hover styles in our stylesheet, our ul#nav:hover li a styles take effect on all li a items except for the anchor we're actually hovering.

    Let's take a look at what we have now!

    It should render properly in all your major browsers (Firefox, Safari, IE7/IE8, Opera, and Google Chrome). As I stated in the beginning of this tutorial, IE6 does not support :hover on elements other than anchors (a:hover) so we'll need to use javascript to get it to behave.

    Get IE6 to Behave

    The way our file is now, IE6 will do all kinds of crazy things. First of all, when you hover over a menu link, you'll notice the icons stick. They won't switch back to display: none when you mouseout. You can fix this by using the visible property instead of the display property, but that won't fix the fact that IE6 doesn't support :hover on elements other than anchors. So we'll just fix them both with a little jQuery.

    Paste the following in the head of your index.html file.

    <!--[if IE 6]>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <script type="text/javascript" src="js/ie6.hoverfix.js"></script>
    <![endif]-->
    

    We're using conditional comments to only include the javascript file we're going to create to users browsing your page with IE6. You could leave out the conditional comments if you wanted to, it wouldn't cause any problems for your other browsers, but it's just good practice to seperate browser specific styles and scripts from your other browsers.

    Create a new file and save it as ie.hoverfix.js in your /js folder. Put the following jQuery in your new file.

    $(document).ready(function() {
    	$("ul#nav li a").hover(function () {		
    		$("span", this).show();
    		$("ul#nav li a").css("background-position","left bottom");
    		$(this).css("background-position","left center");
    	}, function () {		
    		$("span", this).hide();
    		$("ul#nav li a").css("background-position","left top");
    	});	
    });
    

    Explaining exactly what's going on here is beyond the scope of this tutorial, however, if you look at it, you'll notice that we're pretty much doing the same thing with our jQuery as we were with our CSS. We're telling the browser "when you hover over the ul#nav li a shift the background position to left bottom and when you mouseout return the background-position to left top -- oh and also, can you hide the menu icons (span) until I hover over its parent anchor?".

    Final Thoughts

    There you have it! We touched on a number of CSS attributes, properties, and declarations in this tutorial, but the important ones to remember in this tutorial are :hover and background-position. With these two things alone you can do quite a bit with CSS.

    Nick La has a great article on maximizing the use of hover on Web Designer Wall.

    I hope you enjoyed reading this tutorial. If you have any questions/problems with implementing any of it, please don't be afraid to ask in the comments!