InternotesSharing Web Development Techniques

Blossoms

Web Menus

Navigation is one of the key elements in all modern web sites, and making the navigation look attractive and familiar is going to be an important part of your web design.

As with everything else, design boils down to CSS. In modern CSS, you can do practically everything you need for an interesting useful navigation system, including some simple animation.

Generally we refer to site navigation as a menu, though we tend to use this term rather loosely.

In this article we will look at creating two simple menu systems: a simple horizontal or vertical menu and a nested drop-down or pop-out menu.

Although the appearance of the menu is controlled entirely by CSS, we will also need a small amount of JavaScript to finish the nested menus.

Menu Basics

A menu is a collection of anchors. This collection can be as simple as a single paragraph with multiple anchors, but is normally more structured to allow better styling, and better semantics.

Although a number of different structures, such as tables, have been used in the past, the most common and suitable structure is an unordered list:

<ul id="simple">
    <li><a href="#">fruit</a></li>
    <li><a href="#">instruments</a></li>
    <li><a href="#">animals</a></li>
</ul>

This will look something like this:

<ul> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

which does the job, but is not very aesthetically pleasing. We’re going to make this look a bit nicer using a few simple techniques.

Simple Styling

First, we’re going to remove the bullets and indenting:

    ul#simple {
        list-style: none;
        padding: 0;
        margin: 0;
    }

giving:

<ul class="eg01"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

The next thing is to make the links look more like navigation buttons. The temptation is to work on the list items, but the trick is to work on the anchors instead.

Here we will remove the underlines and set a few colours:

    ul#simple>li>a {
        text-decoration: none;
        background-color: darkgreen;
        color: white;
    }

<ul class="eg01 eg02"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

Note the child selector (>) above. This ensures that other descendants don’t accidentally inherit too much, which is not much of a problem here, but will certainly be when we look at nested lists.

This is a start, but pretty ugly. The two main problems are with padding and width. Padding is straightforward, but the width is handled more subtly.

Anchors are normally displayed inline. Here they don’t look inline because their parents, list items, are displayed as blocks. One of the characteristics of inline elements is that they are shrink-wrapped: they always resize to fit their contents (plus any padding you might apply). If you set a width, this will be ignored.

To be able to set a width on an element, you will need to display it as a block (or inline-block, which won’t make any difference here). By default blocks are 100% of the width of their parent, but now you can set an alternative width.

    ul#simple>li>a {
        …
        display: block;
        padding: .5em 1em;
    }

<ul class="eg01 eg02 eg03"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

At this point, we’re ready to choose between a vertical menu and a horizontal menu.

Vertical Menu

Well, the menu is already vertical, but there are three things you can do to finish the job.

First, the menu is full width. If it’s inside another limited-width container, that’s fine. Otherwise we will need to set the width.

Second, it might be ice to have lines between the items. CSS cannot do anything between elements, but we can put lines above all the elements except the first.

In modern browsers, CSS allows you to pick off elements using nth-child with a simple forumula. However, in this case, there is an alternative which will also work in Legacy™ browsers.

The expression li+li means any li which comes after another li. This will be true of all li elements except the first. We will create lines between elements by putting a (white) border on the top of the anchor in these list items:

    ul#simple {
        width: 12em;    /* or leave out to fill its container */
    }
    ul#simple>li+li>a {
        border-top: thin solid white;
    }

Finally, you can change the colour on highlight. On the desktop, it is good enough to highlight on hover, but on mobile devices without a mouse, you can highlight when the item is pressed, for which we use the :active pseudoclass.

ul#simple>li>a:hover,
ul#simple>li>a:active {
    background-color: green;
}

<ul class="eg01 eg02 eg03 eg04"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

Finished CSS

    ul#simple {
        list-style: none;
        padding: 0;
        margin: 0;

        width: 12em;    /* or leave out to fill its container */
    }
    ul#simple>li>a {
        text-decoration: none;
        background-color: darkgreen;
        color: white;

        display: block;
        padding: .5em 1em;

        border-top: thin solid white;
    }

Horizontal Menu

If you prefer a horizontal menu, there are a few additional tricks. The most basic trick is to display the list items horizontally. There are a few ways, but the most straight forward way in recent browsers is to use the table-cell display.

Note that we’re not saying that this is a table (and CSS can’t change the semantics of your HTML). Rather we’re saying that we wish to display something in a similar way to a table.

    ul#simple {
        width: 12em;    /* or leave out to fill its container */
    }
    ul#simple>li {
        display: table-cell;
    }

<ul class="eg01 eg02 eg03 eg05"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

Note that the anchor itself is still displayed as block.

The problem with table display is that, by default, cells are re-sized to fit the content snugly. We want to spread the content out across the screen, or at least the container, but tables don’t work that way by default.

The other problem with table display is how CSS interprets their context.

In a real table, cells belong inside rows and rows belong inside tables. CSS will automatically retro-fit this idea to your content. This means that the parent of a table-cell, in this case the ul list, is expected act as a table-row.

For us, this won’t help, because you can’t control the size or spacing of table row. However, you can with a table itself, and CSS allows you force the issue:

display: table

Armed with this knowledge, we will:

  • Set the ul to display as a table, so that we can:
  • Display the ul full width
  • Spread out the cells evenly

Spreading the cells out evenly is achieved by applying the property table-layout: fixed to the list.

    ul#simple {
        display: table;
        width: 100%;
        table-layout: fixed;
    }

<ul class="eg01 eg02 eg03 eg05 eg06"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

etc

To finish the job, we will add a vertical line between them, change the colour on hightlight, and center the text.

Adding vertical lines between them requires the same trick as the horizontal lines above. We put a border on the left of all but the first item.

To change the colour on highlight, as before, we change the colour on :hover or :acitve.

Centering the text would be ill-advised in the vertical menu since it will make a mess of the vertical alignment. It does make some sense in the horizontal menu since we have the items neatly boxed off horizontally.

ul#simple>li>a {
    text-align: center;
}
ul#simple>li+li>a {
    border-left: thin solid white;
}
ul#simple>li>a:hover,
ul#simple>li>a:active {
    background-color: green;
}

<ul class="eg01 eg02 eg03 eg05 eg06 eg07"> <li><a href="#">fruit</a></li> <li><a href="#">instruments</a></li> <li><a href="#">animals</a></li> </ul>

Drop-Down Menus

Drop down menus are more impressive, of course, but take a little more work. Not much more, though, since we’ve already solved some of the main styling problems.

To begin with, we need a nested list:

<ul id="nested">
    <li>fruit
        <ul>
            <li><a href="#">apple</a></li>
            <li><a href="#">banana</a></li>
            <li><a href="#">cherry</a></li>
            <li><a href="#">donut</a></li>
        </ul>
    </li>
    <li>instruments
        <ul>
            <li><a href="#">accordion</a></li>
            <li><a href="#">banjo</a></li>
            <li><a href="#">clarinet</a></li>
            <li><a href="#">dobro</a></li>
        </ul>
    </li>
    <li>animals
        <ul>
            <li><a href="#">aardvark</a></li>
            <li><a href="#">badger</a></li>
            <li><a href="#">caterpillar</a></li>
            <li><a href="#">dragon</a></li>
        </ul>
    </li>
</ul>

At the top level is the same list as before. Each list item contains the name of a category, but also an inner list of items.

<ul class="eg10"> <li>fruit <ul> <li><a href="#">apple</a></li> <li><a href="#">banana</a></li> <li><a href="#">cherry</a></li> <li><a href="#">donut</a></li> </ul> </li> <li>instruments <ul> <li><a href="#">accordion</a></li> <li><a href="#">banjo</a></li> <li><a href="#">clarinet</a></li> <li><a href="#">dobro</a></li> </ul> </li> <li>animals <ul> <li><a href="#">aardvark</a></li> <li><a href="#">badger</a></li> <li><a href="#">caterpillar</a></li> <li><a href="#">dragon</a></li> </ul> </li> </ul>

Preliminary Styling

For the most part, much of the styling is the same as for the simple menu. However:

  • The basic list styling should be applied to both the main list and the nested list
  • Horizontal layout should only apply to the main list
  • Other visual styling will be applied only to the main list
  • The nested list will have a styling similar to the vertical menu above
ul#nested,
ul#nested ul {
    list-style: none;
    margin: 0;
    padding: 0;
}
ul#nested {
    display: table;
    width: 100%;
    table-layout: fixed;
}
ul#nested>li {
    /*  Main List */
    display: table-cell;

    /*  Visual */
        font-family: sans-serif;

        background-color: darkgreen;
        color: white;
        padding: .5em 0;
        text-align: center;
        position: relative;
        overflow: visible;
}

<ul class="eg10 eg11"> <li>fruit <ul> <li><a href="#">apple</a></li> <li><a href="#">banana</a></li> <li><a href="#">cherry</a></li> <li><a href="#">donut</a></li> </ul> </li> <li>instruments <ul> <li><a href="#">accordion</a></li> <li><a href="#">banjo</a></li> <li><a href="#">clarinet</a></li> <li><a href="#">dobro</a></li> </ul> </li> <li>animals <ul> <li><a href="#">aardvark</a></li> <li><a href="#">badger</a></li> <li><a href="#">caterpillar</a></li> <li><a href="#">dragon</a></li> </ul> </li> </ul>

Hidden Sub-Menus

The trick is to initially hide the inner lists:

ul#nested>li>ul {
    display: none;
}

<ul class="eg10 eg11 eg12"> <li>fruit <ul> <li><a href="#">apple</a></li> <li><a href="#">banana</a></li> <li><a href="#">cherry</a></li> <li><a href="#">donut</a></li> </ul> </li> <li>instruments <ul> <li><a href="#">accordion</a></li> <li><a href="#">banjo</a></li> <li><a href="#">clarinet</a></li> <li><a href="#">dobro</a></li> </ul> </li> <li>animals <ul> <li><a href="#">aardvark</a></li> <li><a href="#">badger</a></li> <li><a href="#">caterpillar</a></li> <li><a href="#">dragon</a></li> </ul> </li> </ul>


<ul id="nested"> <li>fruit <ul> <li><a href="#">apple</a></li> <li><a href="#">banana</a></li> <li><a href="#">cherry</a></li> <li><a href="#">donut</a></li> </ul> </li> <li>instruments <ul> <li><a href="#">accordion</a></li> <li><a href="#">banjo</a></li> <li><a href="#">clarinet</a></li> <li><a href="#">dobro</a></li> </ul> </li> <li>animals <ul> <li><a href="#">aardvark</a></li> <li><a href="#">badger</a></li> <li><a href="#">caterpillar</a></li> <li><a href="#">dragon</a></li> </ul> </li> </ul>