Tabular menu items in Drupal

tabs

In today’s excercise, we are going to adapt some nice styles to the Drupal menu items. There is a widely known CSS technique called sliding doors which became very popular since it became published. We are going to spice this a little more with extra hover states and implement sprites to improve the performance a little. This will involve a little PHP code – overriding a themeable function, and some CSS markup.

Let’s start with a piece of generated html code:

<ul class="links-menu">
  <li><a href="/free">Download templates</a></li>
  <li><a href="/about">About</a></li>
  <li class="active"><a href="/blog" class="active">Blog</a></li>
  <li><a href="/contact">Contact</a></li>
</ul>

This is how Drupal renders primary links. Notice the active menu item which is set by a CSS class active on both the <li> and <a> tags. Since the anchor element’s parent is set to a class, it makes extremely easy to theme nice menu items.

Let’s create a background image for the tabs. Our tab will have four states: one for a regular item, one for hover, and both of them once they’re .active. I prefer using sprites as this will improve performance a little: loading several little images takes more time than loading one bigger image which holds all the necessary background elements.

So I decided to come up with this tab image.

As you can see, it holds all the four states and includes the right slice and the left slice of the background. The right element applied to tag a and the left to tag li. This is very easy, but there comes IE which cripples down every great CSS idea since it can only apply hovers to element a. Now what? As we still need two different elements to be hovered, we include a span element inside every a.

Thus we need a HTML like this:

<ul class="links-menu">
  <li><a href="/free"><span>Download templates</span></a></li>
  <li><a href="/about"><span>About</span></a></li>
  <li class="active"><a href="/blog" class="active"><span>Blog</span></a></li>
  <li><a href="/contact"><span>Contact</span></a></li>
</ul>

Now we can reach the span tag via CSS using a span and a:hover span declarations. To have Drupal generate this HTML code we need to add a little function to the template.php file of your theme.

<?php
/*  override item links with injecting span tags in between the link content  */
function yourtheme_menu_links($links) {
  if (!
count($links)) {
    return
'';
  }
 
$level_tmp = explode('-', key($links));
 
$level = $level_tmp[0];
 
$output = "<ul class=\"links-$level\">\n";
  foreach (
$links as $index => $link) {
   
$output .= '<li';
    if (
stristr($index, 'active')) {
     
$output .= ' class="active"';
    }
   
$output .= ">". l("<span>".$link['title']."</span>", $link['href'], $link['attributes'], $link['query'], $link['fragment'], FALSE, TRUE) ."</li>\n";
  }
 
$output .= '</ul>';

  return
$output;
}
?>

Of course you have to put your theme name where ‘yourtheme’ was used above. I only copied the standard function and modified the $output line a little. According to the function l we had to set the last switch to TRUE since the link text now includes HTML (the span tags).

Now let’s see the CSS code. I won’t explain fancy stuff from now on, just copy & paste my css used in Ubiquity for tabs. As we’re theming primary links, we encapsulate every style declaration in the #primary div to avoid globalizing these settings.

First, position and style the menu items:

#primary {
  float: left;
  margin: .75em 0 0 0;
}
#primary .links-menu {
  margin: 0;
  padding: 0 2em;
  list-style: none;
  border: 0;
}
#primary .links-menu li {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
}
#primary .links-menu li a {
  display: inline;
  float: left;
  margin: 0;
  padding: 0 0 0 4px;
  border: 0;
  background: transparent url("images/tabs.png") no-repeat left top;
  text-decoration: none;
}
#primary .links-menu li a span {
  display: block;
  background: transparent url("images/tabs.png") no-repeat right top;
  padding: 5px 13px 5px 5px;
  color: #039;
}
/* Commented Backslash Hack hides rule from IE5-Mac \*/
#primary .links-menu li a span { float: none; }
/* End IE5-Mac hack */

#primary .links-menu li a:hover span {
  color: #006;
}

Notice the brilliant Mac IE 5 Backslash Hack which promotes IE 5 on Mac still a usable browser :)
Now here comes the part dealing with hovers and active states:

#primary .links-menu li a:hover {
  background-color: transparent;
  background-position: 0% -50px;
}
#primary .links-menu li a:hover span {
  background-position: 100% -50px;
}
#primary .links-menu li.active a {
  background-color: transparent;
  background-position: 0% -100px;
}
#primary .links-menu li.active a span {
  background-position: 100% -100px;
  font-weight: bold;
}
#primary .links-menu li.active a:hover {
background-color: transparent;
  background-position: 0% -150px;
}
#primary .links-menu li.active a:hover span {
  background-position: 100% -150px;
  font-weight: bold;
}

As you can see, we can do hovers with span tag inside the anchors and dealing with sprites is very easy, just tell the proper background positions in each state and you’re set to go.

Download the sample code, experiment with it, and let me know how far you went with it.

This is a great example and

This is a great example and will sure help a lot of people with the source code released too.

Thanks a lot!!

I spent a lot time looking for how to manipulate the primary links menu, thanks.

Thanks

Great post, and point taken. I think this article done a great job.What a best way to describe your view. Thanks for sharing with us. Really like your informative article. Hopefully we will get more interesting topic from you in future.

"active" class no longer added to <li> tag??

Hi,

Cheers for the excellent article. I'm just starting out themeing drupal and this was very helpful.

I'm running drupal 5.6 and it seems that the active class is no longer added to the li tag...

This seems to be the case with Garland and Zen subthemes. This means the active tab is not highlighted. I tweaked your code and came up with the following for the active bits - i.e. I just moved the active from the li to the a tags.

Oh yeah, I'm also damn confused as to whether to use theme_menu_links or theme_links to modify my html. Garland, Zen etc use theme_links so I did too. This function adds the class-name links and not menu-links and so that's why my targets below use .link and not menu-links like yours.


#primary .links li a.active {
background-color: transparent;
background-position: 0% -100px;
}
#primary .links li a.active span {
background-position: 100% -100px;
font-weight: bold;
}
#primary .links li a.active:hover {
background-color: transparent;
background-position: 0% -150px;
}
#primary .links li a.active:hover span {
background-position: 100% -150px;
font-weight: bold;
}

The bit that worried me was the hover sudo-class added to the a.active in the second-last target. Seems to work on FF and IE7.

Am I completely off the track here when I say that the active class is no longer added to the li tags in primary links???

Here's the output using the Garland theme:


<ul class="links primary-links">
<li class="first menu-1-1-2">
<a class="menu-1-1-2" href="/drupal-5.6/taxonomy/term/3">Another Link
</li>
<li class="last menu-1-2-2">
<a class="menu-1-2-2 active" href="/drupal-5.6/">test link
</li>
</ul>

Thanks again for a great clear article.
-Ben.

WOW! Nice

I spent some time searching the drupal site trying to figure this out, and couldn't find anything, then just randomly fell upon this post, and it has given me GREAT insight into fixing this for a client (previously I just manually configured the top menu).

THANKS!

-TrevorLee