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 amout 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 dirrerent structures, such as tables, have been used in the past, the most common and suitable structure is an unordered list: code,html---------------------------------------- code,html ---------------------------------------- This will look something like this: fruit instruments animals 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: code,css------------------------- code,css ul#simple { list-style: none; padding: 0; margin: 0; } ------------------------- giving: fruit instruments animals 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: code,css------------------------------------ code,css ul#simple>li>a { text-decoration: none; background-color: darkgreen; color: white; } ------------------------------------ fruit instruments animals 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. code,css-------------------------------- code,css ul#simple>li>a { … display: block; padding: .5em 1em; } -------------------------------- fruit instruments animals 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: code,css---------------------------------------------------------------- code,css 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. code,css---------------------------- code,css ul#simple>li>a:hover, ul#simple>li>a:active { background-color: green; } ---------------------------- fruit instruments animals Finished CSS ++++++++++++ code,css---------------------------------------------------------------- code,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. code,css---------------------------------------------------------------- code,css ul#simple { width: 12em; /* or leave out to fill its container */ } ul#simple>li { display: table-cell; } ---------------------------------------------------------------- fruit instruments animals 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. code,css---------------------------- code,css ul#simple { display: table; width: 100%; table-layout: fixed; } ---------------------------- fruit instruments animals 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. code,css---------------------------------- code,css 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; } ---------------------------------- fruit instruments animals 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: code,html------------------------------------------------ code,html ------------------------------------------------ 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. fruit apple banana cherry donut instruments accordion banjo clarinet dobro animals aardvark badger caterpillar dragon 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 lit will have a styling similar to the vertical menu above code,css------------------------------------ code,css 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; } ------------------------------------ fruit apple banana cherry donut instruments accordion banjo clarinet dobro animals aardvark badger caterpillar dragon Hidden Sub-Menus ^^^^^^^^^^^^^^^^ The trick is to initially hide the inner lists: fruit apple banana cherry donut instruments accordion banjo clarinet dobro animals aardvark badger caterpillar dragon ''''' fruit apple banana cherry donut instruments accordion banjo clarinet dobro animals aardvark badger caterpillar dragon