CSS3: Indicate active menu item with a triangle

Posted by on

Today on StackOverflow a user asked the following question:

In the following picture, I have mocked up a CSS active menu styling I would like to find examples of, and hopefully replicate.

Navigational menu with a triangle indicating active item

You will notice there is a triangle highlighting the active menu, and it can appear bold too.

Question 1. What is this “active menu triangle style” actually called? So I can find CSS examples of it.
Question 2. Where can I find examples of this?

Thanks.

Source: What is this CSS active menu style with triangle called?

While I don’t know what this kind of menu would exactly be called, it did get me thinking about how to solve this using only CSS, so preferrably without background images.

My first thought was to use a css border triangle, but those only come in one solid color per triangle. This one needed to be a white triangle with a border, which can’t be achieved with the border slopes method. So I figured I’d just have to append an element looking like a triangle, using the :after pseudo-element. I’ll explain how I did that.

The menu

First of all, I recreated the menu by making an unordered list with one active item, plus an extra background color to more easily recognize the active item:

HTML:

<ul>
  <li>One</li>
  <li class="active">Two</li>
  <li>Three</li>
 </ul>

CSS:

li {
  background: #ddd;
  border-bottom: 1px solid black;
  display: block;
  float: left;
  list-style: none outside none;
  padding: 8px 15px;
  text-align: center;
  width: 50px;
}
li.active {
 background: white;
 font-weight: bold;
}

Giving a menu like this:

Standard CSS menu with items floating left
(all my screenshots were made in Chrome 8 on Windows 7. If the final results look a lot different to you than they do in this post, please leave a comment).

Simulating an arrow

Then I simulated an arrow by creating a small square div with only a top and right-side border. By rotating this block counterclockwise by -45 degrees I made it look like an arrow pointing up. By setting different borders or rotating it in other directions you can of course make the arrow point anywhere, but for now I only needed an arrow pointing up.

By appending an :after pseudo-element to the active list element I could insert this arrow-simulating block into the menu without actually having to use an extra element into the source code.

CSS:

li.active:after {
    border: solid black;
    border-width: 1px 1px 0 0;
    content: ' ';
    display: block;
    height: 10px;
    position: absolute;
    width: 10px;
    z-index: 99;
    -webkit-transform: rotate(-45deg);
    -webkit-transform-origin: 50% 50%;
}

There’s a few things I might have to explain about this CSS. First of all, I added the content: ' '; property to fill the simulated element with some content, otherwise it sometimes doesn’t show up. An alternative is to add a character (e.g. a dot) and set it’s color to transparent, but in this case the space worked fine. The position has to be absolute to prevent the arrow from interfering with the flow of the menu. This has one big downside that I’ll explain a little further down. It also means the li has to have a position: relative to keep the absolute positioning within the bounds of the list item.

Then of course there’s the transform property to rotate the element. In my case I used only the -webkit-transform because I work in Chrome. To support all browsers you’d have to add vendor prefixes for Mozilla and Opera as well, which I’ll do in the final code. The transform-origin is set to 50% 50%, to make sure the block’s pivotal point is the exact center. It’s also possible to rotate it around for example a corner, or another arbitrary point, but this way we have the most control over it.

Here’s what it looks like now:

CSS pseudo element acting as a triangle, almost finished

Finishing touches

Almost finished, but not yet. We’ll now have to position the arrow to the center of the active menu item, and here’s where the aforementioned problem pops up. Because we positioned the arrow absolutely we have a lot of control over where to place it, except that we can’t let it center dynamically. Relatively positioned elements can have their margins set to auto to let them center, inline elements respond to text-align: center, but this arrow can’t be centered. Fortunately, for my example I used list items with a fixed width, but if your list items have dynamical widths it gets very tough to actually center the arrow.

Red dots to show where the arrow's borders go

Outline of the entire block

Also, as you can see the bottom border of the active list item is still present, even there where the two borders from the arrow’s block would be if we had set them (see image). To make sure the bottom border is invisible there where the arrow is positioned we give the block a background color, in this case white.

This also means that where the block goes below the list item’s boundaries an inversed arrow (the borderless part of the block) will appear. In our case this is not a problem because both the block and the background are white. If the surroundings of the menu are another color, you might have to set ul { overflow: hidden } to hide the overflowing part of the block.

Final CSS

With the extra fixes, here’s the full code to make this menu work including an effect on hover:

CSS:

ul { overflow: hidden; }

li {
 background: #ddd;
 border-bottom: 1px solid black;
 display: block;
 float: left;
 list-style: none outside none;
 padding: 8px 15px;
 position: relative;
 text-align: center;
 width: 50px;
}

li.active,
li:hover {
  background: white;
  font-weight: bold;
}

li.active:after,
li:hover::after {
 background: white;
 border: solid black;
 border-width: 1px 1px 0 0;
 bottom: -5px;
 content: ' ';
 display: block;
 height: 10px;
 left: 32px;
 position: absolute;
 width: 10px;
 z-index: 99; 

 -webkit-transform: rotate(-45deg);
 -webkit-transform-origin: 50% 50%;
 -moz-transform: rotate(-45deg);
 -moz-transform-origin: 50% 50%;
 -ms-transform: rotate(-45deg); /* IE 9 */
 -ms-transform-origin:50% 50%; /* IE 9 */
 -o-transform: rotate(-45deg);
 -o-transform-origin: 50% 50%;
 transform: rotate(-45deg);
 transform-origin: 50% 50%
}

And here’s a final picture:

CSS triangle on active menu item, final version

I also made a jsFiddle expample for you to play with.

Was this useful for you? Do you know some fixes to make this even better? Let me know in the comments!

4 Responses to “CSS3: Indicate active menu item with a triangle”

  1. David said on

    Thanks very much Stephen great tutorial

    You may want to add this to the end of your browser rotates.

    -ms-transform: rotate(-45deg); /* IE 9 */
    -ms-transform-origin:50% 50%; /* IE 9 */

    Cheers

  2. Stephan said on

    Ah, thanks! I was aware that IE9 uses it’s own prefix, but it hadn’t occured to me yet to update this post :)

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>