Simpler CSS Sliding Doors

Update: This article is old. Browser support for CSS border-radius and box-shadow is pretty good now - use them if you can and don't hesitate to give older browsers a less polished page.

The canonical Sliding Doors of CSS by Douglas Bowman names two methods for aligning a list of tabs in a horizontal row:

I think the inline method is simpler, and since I haven't seen it documented anywhere, I'm going to do that now. I'll also show how to implement the tabs with only one image.

The image

We create our single tab image by appending the images from Bowman's article so there is a normal tab with a highlighted tab below it. The vertical offset of the highlighted tab is 150px. Here's the final image:

Background image used for tabs.

Tab creation

Let's start with the same markup as in Bowman's article:

<div id="header">
  <ul>
    <li><a href="#">Home</a></li>
    <li id="current"><a href="#">News</a></li>
    <li><a href="#">Products</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</div>

First pass

We make the list and list-items display inline, remove the list-item markers and set the default margins and paddings to zero.

#header ul, #header li {
  display: inline;
  list-style: outside none;
  margin: 0;
  padding: 0;
}

Next we attach the background image to the list-items and anchors. As in Bowman's article, we align the image at top-left for the list-item and top-right for the anchor.

#header li, #header li a {
  background-image: url(button.gif);
}
#header li {
  background-position: left top;
}
#header li a {
  background-position: right top;
}

We also provide some padding to push the tab-text away from the borders. The top and bottom padding is to be 0.5em and the left and right padding is to be 10px. Because these elements are being displayed inline, the same top and bottom padding must applied to both list-items and anchors, but the left and right padding is split between them.

#header li, #header li a {
  padding-top: 0.5em;
  padding-bottom: 0.5em;
}
#header li {
  padding-left: 10px;
}
#header li a {
  padding-right: 10px;
}

Finally we give the whole tab-container a bottom border and zero the margins and padding.

#header {
  border-bottom: 1px solid #aaaaaa;
  margin: 0;
  padding: 0;
}

Let's see how that renders. Don't worry about Internet Explorer for now - view the following demo with Firefox, Safari or Opera.

Fix for non-IE browsers

Not a bad start, but there are a few issues:

  1. the bottom border of the tab container is not at the bottom of the tabs but at the bottom of the text. In fact, the top border is also aligned to the top of the text, which you can see if you check the "Show tab-container border" option in the demo (assuming you have Javascript enabled).
  2. there is a space of four pixels between the tabs (which may not be a problem - you might prefer it).
  3. the left-most 10px of each tab is unclickable since we are using a padding on the list-item. You can see the clickable region of each tab if you check the "Show anchor border" option in the demo.

The tab-container border is aligned to the text because the list and list-items have inline rather than block display styles. This can be fixed by either setting the line-height or the top and bottom padding of the tab-container, such as:

#header {
  /* top and bottom padding are the same as for inline list-item */
  padding-top: 0.5em;
  padding-bottom: 0.5em;
  /* alternate method with line-height equal to text height plus top and bottom padding 
  line-height: 2em;
  */
}

To fix the spacing between tabs we have at least the following options:

#header ul, #header li {
  margin-right: -4px;
}

We also have a few options for fixing the clickable region of tabs.

#header li {
  padding-left: 5px;
}
#header li a {
  padding-left: 5px;
}

Let's view that again.

It should look right in Firefox, Safari and Opera, but seem like a mess in Internet Explorer.

Fix for IE

In fact, Internet Explorer just requires some tweaks to paddings and margins. We remove the top and bottom paddings on the list-items by explicitly setting them to zero. And we remove the negative offset on the right margins of the list and list-items and add it to the right of the anchors.

Note that because these styles are only targetting Internet Explorer they are encapsulated in conditional comments. In an external stylesheet they could be hidden from other browsers with various CSS hacks.

<!--[if lte IE 7]>
<style>
#header li {
    padding-top: 0;
    padding-bottom: 0;
}
#header ul, #header li {
    margin-right: 0;
}
#header li a {
    margin-right: -5px;
}
</style>
<![endif]-->

Let's verify that solution.

It seems to be working well across browsers now - Firefox, Safari, Opera and Internet Explorer.

Highlighting

Now we have to implement the highlighting of the current tab and the hovered tab. As stated previously, the highlighting image is already part of the background-image but offset vertically by 150px. To apply the highlight we offset the background-position style property of applicable elements by -150px.

#header li.current, #header li:hover {
  background-position: left -150px;
}
#header li.current a, #header li:hover a {
  background-position: right -150px;
}

This won't achieve the hover effect in IE6 because that browser only accepts :hover styles for anchors. If you want the hover effect in IE6 you could:

Anyway, we are basically done now. Let's just check that the highlighting works.

Follow-up

More issues

There are still a few potential issues with this approach which I will only demonstrate for now.

Transparent backgrounds

The background-image that we have been using has a white background and relies on the tab container also having a white background. You can see this by selecting the "Show dark background" option in the last demo.

If you want a different background color then you can:

Transparent backgrounds seem a better approach, but they also have a few issues, especially over which image format you should use. Good looking rounded corners seem to require more than binary transparency, but achieving that on IE6 is complex:

Narrow tab-containers

The approach I have illustrated in this article hasn't been adjusted to cope with narrow tab-container widths. You can see this by selecting the "Reduce tab-container width" option in the last demo.

Other approaches

You can also use inline-block display styles on the list-item and anchor elements. This would be a simpler approach except that the history of browser support is quite complex necessitating as many work-arounds as the other approaches.

Rounded borders can also be implemented using CSS3 border-radius style properties, however the background-image approach allows for more complex tab designs.

Future work

Apart from addressing the issues raised in this section, it might be useful to have a web-application that generates the tab-images and stylesheets based on user specified colors, border-radius, etc.