Single image replacement rollovers with Suckerfish dropdowns

By John Faulds

I discovered two of my most often used CSS techniques at the same place - Petr Staníček's Wellstyled.com (Staropramen, Budvar, Pilsner Urquell, great CSS techniques - gotta love the Czechs ;) ).

And those techniques are an image replacement (IR) technique that I later found out was actually called the Gilder Levin method and the use of a single image for creating CSS rollovers without the need for preloading images.

What was missing from the two tutorials, however, was an example that showed how the two could be combined. Something else that I couldn't find a tutorial for when I first went looking at how to do it was a way to combine the previous two examples when making a navigation bar. In other words, using a single image for both normal and hover states for all links in an image-replaced navigation bar. And then adding to that the added accessibility of incorporating the Gilder Levin IR technique.

I still haven't come across a tutorial that combines all that, so I thought I'd do one myself to help out anyone who might be looking to do the same thing.

I'm also going to provide an example of how to incorporate all of that and add another popular CSS technique to the mix as well - Sons of Suckerfish Dropdowns.

First of all, it might help to look at the example to give you an idea of what I'm talking about. The image that we're going to be using to create the navigation bar (horizontal in our case, but the same techniques could be applied to a vertical example) is below:

Links image

The HTML

The HTML for this is fairly simple:

<ul class="nav IR">
  <li class="link1">
    <a href="#"><em></em>Link 1</a>
  </li>
  <li class="link2">
    <a href="#"><em></em>Link 2</a>
  </li>
  <li class="link3">
    <a href="#"><em></em>Link 3</a>
  </li>
</ul>

Without any styling, it gives us this:

The class="nav IR" is because we’re attaching two classes to the list: one for styles specific to the navigation bar we’re creating and the second with general image replacement styles that can be used elsewhere (either on the same page or across a site).

The classes for each of the links are so that we can style each link with its individual image. I’d normally use IDs for this sort of list, but as the example shows two variations of the same list, I’ve used classes instead. (IDs can only be used once per page. If you want to reuse styles, you need to use classes.)

The CSS

So, let’s look at the CSS:

ul.IR li {
  position: relative;
  / *overflow: hidden;
  commented out for this example */
  font-size: 0.9em;
}

.IR em {
  display: block;
  position: absolute;
  top: 0; left: 0;
  z-index: 1;
}

/* For IE5.x mac only */
* html>body .IR {
  position: static;
  / *overflow: visible;
  commented out for this example */
  font-size: 10px;
}

* html>body .IR em  { position: static; }

The position: relative on ul.IR li is to establish a new positioning context for the elements contained within. In this case, it’s the em which we’re going to place at the top corner of each list element with position: absolute so that it in fact covers up the text underneath. We need to set the em to display: block because it’s an inline element and won’t take dimensions in its default state.

The IR technique I’m using is actually an updated version of the one that I first discovered on Wellstyled.com and irons out a few bugs that IE5/Mac had with the original. The updated method is called the Gilder Levin Ryznar Jacoubsen IR method. I’ve removed a couple of styles relating to overflow: hidden for this example because it will prevent our Suckerfish dropdowns showing in the second example. The overflow: hidden is to prevent the text peeping underneath the images when you resize the text and it can be controlled fairly well by setting an appropriate font-size instead.

The * html>body styles only affect IE5/Mac and the explanation of why they’re necessary is given in full at the link above.

So that sets up our IR styles, now we can start styling the list itself.

.nav { list-style: none; }
.nav li { float: left; padding-bottom: 10px; }
.nav li, .nav li em { width: 147px; height: 27px; }

First, we remove the bullets from the list, then we float the list items to the left so that they are all on the same horizontal plane. We then give the same dimensions to both the list items and the ems that will hold the images. In this case, the height is a little over half the height of our actual image because we only want the top half to appear in the normal state (the rollover state remains hidden below). The actual width of the whole image is 441 pixels, but we only want a third of that to appear within each link. (All my links are the same width because the word is nearly the same, but for real-life situations with words of different lengths, you’d probably need to give each list item and its associated em a different width.) A little bit of bottom padding may also be required to give the list item area enough depth to ensure that the submenus don’t disappear too quickly when trying to roll over them.

.link1 em, .link2 em, .link3 em {
  background: url(links.gif) no-repeat;
  cursor: pointer;
}

Then we apply the background-image to each of the ems. We need to apply the background-image to each individually because we’re going to be manipulating the background-position of the image for each one (see below).

The cursor: pointer; is needed for IE which won’t show the normal ‘hand’ cursor when hovering over the links with this IR method. (For IE5+ you need to use cursor: hand; which, unfortunately, doesn’t validate, so you may want to move it to an IE5-only stylesheet accessed by conditional comments.)

Now, we’re going to position the background image in each list item.

.link1 em { left: 0; }
.link1 em {
  background-position: 0 0;
}
.link1:hover em {
  background-position: 0 -27px;
}
.link2 { left: 17px; }
.link2 em {
  background-position: -152px 0;
}
.link2:hover em {
  background-position: -152px -27px;
}
.link3 { left: 46px; }
.link3 em {
  background-position: -314px 0;
}
.link3:hover em {
  background-position: -314px -27px;
}

For the first item, we want it and the background image to be at the 0 position both horizontally and vertically. The li:hover moves the background image vertically, moving the normal state out of view and bringing the rollover state on screen.

IE doesn’t accept :hover on any elements other than pseudo elements. To get it to work for other elements, we need something like the whatever:hover and attach it to the body like so:

body { behavior: url(hover.htc); }

For the second and third list item, we move the position of the list item horizontally and then use a negative horizontal position to bring the right part of the image into view. A good way of getting the positions right is to place the actual image temporarily on your page while you’re getting the layout right so that you can line up your menu with the image.

And what we end up with is Example 1.

Adding the dropdown

Now, we’re going to add the Suckerfish dropdown. I won’t go into the exact workings of it here, because it’s been done more than adequately at HTML Dog. I have removed a lot of the styles in the examples given there because they weren’t necessary for the look I was trying to achieve. I’ve also dispensed with the javascript that they’re using because the whatever:hover does the same thing in my example.

First we need to amend the HTLM to include the dropdown menus:

<ul class="nav IR">
  <li class="link1">
    <a href="#"><em></em>Link 1</a>
    <ul>
      <li><a href="#">Submenu 1.1</a></li>
      <li><a href="#">Submenu 1.2</a></li>
      <li><a href="#">Submenu 1.3</a></li>
    </ul>
  </li>
  <li class="link2">
    <a href="#"><em></em>Link 2</a>
    <ul>
      <li><a href="#">Submenu 2.1</a></li>
      <li><a href="#">Submenu 2.2</a></li>
      <li><a href="#">Submenu 2.3</a></li>
    </ul>
  </li>
  <li class="link3">
    <a href="#"><em></em>Link 3</a>
    <ul>
      <li><a href="#">Submenu 3.1</a></li>
      <li><a href="#">Submenu 3.2</a></li>
      <li><a href="#">Submenu 3.3</a></li>
    </ul>
  </li>
</ul>

And the additional CSS:

.nav a { display: block; }
/* The Holly Hack */
* html .nav a { height: 1%; }

.nav li ul {
  position: absolute;
  z-index: 10; /* show the
  dropdowns above the images */
  top: 28px; /* position the dropdowns a set distance from the top of the image */
  left: -999em;
  list-style: none;
  border: 1px solid #FFCC00;
}

.nav li:hover ul { left: auto; }

We set all the anchor tags to display: block; so that they’re all the same width. We use the Holly Hack to correct a hasLayout problem with IE6 which led to problems with accessing the dropdown links.

We have already set the positioning context for the image replaced list items, so the descendant unordered lists – the dropdowns – are placed absolutely relative to their parents. The left: -999em; places them off-screen in their normal state with the li:hover bringing them back into view.

.nav li li {
  height: auto;
  padding-bottom: 0; /* reset the height set on the IR list items */
  font-size: 110%;
  border-bottom: 1px solid #FFCC00;
}

We need to reset the height and padding on the descendant list items because they were set for the image replaced list items. The rest of our styles are simply to get the dropdown list items to look the way we want.

.nav li ul a {
  padding: 0.25em;
  color: #FF6600;
  background: #FFFFCC;
}
.nav li ul a:hover {
  color: #CC0000;
  background: #FFCC00;
}

And that’s it. As mentioned previously, in real-world situations there’s probably quite a few things that would change, particularly to do with the width of the image replaced list items and maybe their descendant dropdowns too.

(The original HTMLDog Suckerfish example and this one fail in IE5/Mac. If you’re concerned to get it right in that browser too, you’ll need to modify the javascript involved.)