marcelfahle.com

How to build a pixel precise horizontal navigation with overlapping active state graphics using CSS

Yesterday I ran into a problem while building a horizontal navigation for a new project I’m working on. On the first look, it looks like a common horizontal navigation based on images:

Fig. 1: All Buttons in normal mode)
Fig. 1: All Buttons in “normal mode”

This is my markup (simplified):

HTML:

  1. <body id=“home”>
  2. <ul>
  3.     <li id=“nav-home”><a href=“index.php?article_id=1″>Home</a></li>
  4.     <li id=“nav-profil”><a href=“index.php?article_id=2″>Profil</a></li>
  5.     <li id=“nav-leistungen”><a href=“index.php?article_id=3″>Leistungen</a></li>
  6.     <li id=“nav-referenzen”><a href=“index.php?article_id=4″>Referenzen</a></li>
  7.     <li id=“nav-kontakt”><a href=“index.php?article_id=5″>Kontakt</a></li>
  8. </ul>
  9. </body>

and the CSS:

CSS:

  1. #navWrap ul {
  2.    margin:0;
  3.    padding:0;
  4.    list-style:none;
  5. }
  6. #navWrap li {
  7.    float:left;
  8.    margin:0;
  9.    padding: 0;
  10.    width: 96px;
  11.    height: 93px;
  12. }
  13. #navWrap a {
  14.    display: block;
  15.    width:100%;
  16.    text-align: center;
  17.    padding: 40px 0;
  18.    text-indent: -5000px;
  19. }
  20. #navWrap ul {
  21.    margin:0;
  22.    padding:0;
  23.    list-style:none;
  24. }
  25. #navWrap li {
  26.    float:left;
  27.    margin:0;
  28.    padding: 0;
  29.    width: 96px;
  30.    height: 93px;
  31. }
  32. #navWrap a {
  33.    display: block;
  34.    width:100%;
  35.    text-align: center;
  36.    padding: 40px 0;
  37.    text-indent: -5000px;
  38. }
  39. #navWrap> ul a {
  40.    width: auto;
  41. }
  42. #nav-home {   background: url(../pics/btn-home-normal.gif) no-repeat; }
  43. #nav-profil { background: url(../pics/btn-profil-normal.gif) no-repeat; }
  44. #nav-leistungen { background: url(../pics/btn-leistungen-normal.gif) no-repeat; }
  45. #nav-referenzen { background: url(../pics/btn-referenzen-normal.gif) no-repeat; }
  46. #nav-kontakt { background: url(../pics/btn-kontakt-normal.gif) no-repeat; }

As you can see, nothing really challenging. Just a standard navigation based on Phark Image Replacement combined with this little but useful Clickable Region Fix by Doug Bowman explained in Sliding Doors of CSS Part II.

So far so good. Here comes the tricky part: the active states. As shown in the picture below, an active state looks like a little offset with shadows on both sides (except the last button „Kontakt“, it only got a shadow on the left) which makes the active state graphics slightly bigger than the normal button graphics (Fig.4).

mf-horiznavoverlap-02.jpg
Fig. 2: “Home” button is active, shadows on both sides. Consider that the top part of the left shadow looks different than the top part of the right shadow because of different background colors: just white on the left and the gray top bar from the “Profil” button on the right side

mf-horiznavoverlap-03.jpg
Fig. 3: “Kontakt” button is active, shadow just on the left side.

mf-horiznavoverlap-04.jpg
Fig. 4: The two Buttons differ in size.

My problem was, how to put the active state button into my navigation without affecting the other pixel precise placed buttons in my navigation.

Here is my solution: Just assign the active state graphic to the <a> element inside the <li> element, so that graphics are not swapped like in other techniques but the active state graphic overlaps the normal one. By setting its position property to absolute, we take the <a> element out of the document flow and ensure, that it can be displayed bigger than its parent <li> element. After assigning its total width (106px) and correcting its position a little bit to the left (margin-left: -5px;) we’re done:

CSS:

  1. #home #nav-home a { background: url(../pics/btn-home-over.gif) no-repeat; margin-left: -5px; width: 106px; position: absolute; }
  2. #profil #nav-profil a { background: url(../pics/btn-profil-over.gif) no-repeat; margin-left: -5px; width: 106px; position: absolute; }
  3. #leistungen #nav-leistungen a { background: url(../pics/btn-leistungen-over.gif) no-repeat; margin-left: -5px; width: 106px; position: absolute; }
  4. #referenzen #nav-referenzen a { background: url(../pics/btn-referenzen-over.gif) no-repeat; margin-left: -5px; width: 106px; position: absolute; }
  5. #kontakt #nav-kontakt a { background: url(../pics/btn-kontakt-over.gif) no-repeat; margin-left: -5px; width: 100px; position: absolute; }

Click here to see it in Action
Yes, it’s maybe not a very revolutionary technique but worked for me in this case. If you got any optmization hints or other approches how to solve the problem, let me know. I would really appreciate that.

Comments:

  1. 1.

    Comment by Lee:

    This looks very cool. Is there anywhere to see it in action?

  2. 2.

    Comment by Marcel:

    Hey Lee,

    right now, I’m working very hard on it. I think it should be finished next week or so. If you’re interested in, I’ll drop you a line when it goes public.

  3. 3.

    Comment by Lee:

    Thanks, Marcel. Would appreciate that. Cheers.

  4. 4.

    Comment by tim:

    how do you create the shadows? css?

  5. 5.

    Comment by Marcel:

    Hi Tim,

    the shadows are part of the images, made in Photoshop, thats why the images are bigger than the ones without shadow (normal state). That is of course the subject of this article, to show how to use active state images which are bigger than the normal ones, without getting aligning problems..

  6. 6.

    Comment by Jason:

    Marcel,

    Would you please let me know when you have posted a working example? Thanks!

  7. 7.

    Comment by j. brotherlove:

    This looks great. I also would like to know if you get it working in modern browsers.

  8. 8.

    Comment by Marcel:

    Hey guys, sorry for that. I’ll put a working example online in the next few hours and send you an email.

  9. 9.

    Comment by Marcel:

    So, here it is:
    http://marcelfahle.com/labs/css/2006-06-25-01

    Sorry for the delay, I had to extract it a little bit from the CMS.

  10. 10.

    Comment by Müfit Kiper:

    Nice solution.
    A real-life example that shows how powerful CSS is.
    Probably gonna find a project to use it in soon.
    Cheers!

  11. 11.

    Comment by MondoBlog » Blog » Fast CSS Links Collection - Num 1:

    […] How to build a pixel precise horizontal navigation with overlapping active state graphics using CSS […]

  12. 12.

    Comment by Mike:

    Im having trouble getting this work, if anybody could help me out that would be great.. im in need of help quickly Please some one help me.. Thanks in advance.

    Mike

  13. 13.

    Comment by simo:

    hey thats pretty damn badass :)