Developer's manual

CSS development

Yet another advantage of using a list-based structure is that it's easy to make precise changes to the menu styling, beyond what's available in the configuration file, simply by addressing the list elements from another stylesheet. This section explores some of the possibilities.

Class, ID and style attributes

You can add these attributes to any of the elements in the tree. As we go through this chapter we'll look at various elements, and discuss what kind of styling you might want to do with them. Here I'm simply introducing the attributes with a view to which is most suitable in a given case:

Class attributes
A class name can be given to multiple elements, so they're used for applying CSS to groups of elements at a time.
ID attributes
An ID must always be unique, so in CSS they're used for applying styles to a single element. ID selectors in CSS also have a higher specificity than Class selectors.
Style attributes
Style attributes are used to apply inline CSS to single elements directly. They have the highest specificity.

How existing styles are applied

Each link's visual styles are applied directly to anchors - the whole box is the link. The list-items are largely unstyled, apart from some 'invisible' properties such as margins between items. In practise this means that most of the external styling you do will be applied to anchors.

There are, however, some situations where you might want to style the list-items as well, to specific effect such as creating menu dividers. We'll talk more about that later, but first lets look at styling the navbar links.

Styling the navbar externally

You first need an id on each of the links you want to style. So start with something like this:

<ul id="udm" class="udm">
	<li><a id="homeButton" href="/">Home</a>
	</li>
	<li><a id="aboutButton" href="/menu/">About</a>
	</li>
	<li><a id="contactButton" href="/contact/">Contact</a>
	</li>
</ul>

Straight away you can see how easy it's going to be; all we need is an id-selector:

#homeButton {
	...
	}

Individual item styles

Applying styles to each of our links is simple enough, but the script handles three different states - unvisited, visited and rollover, in that order. Here's how each state can be addressed:

the default state
This is addressed with the id-selector alone, as we've just seen. Omitting the :link pseudo-class means that styles defined here can apply to all states, unless they're otherwise defined:
#homeButton {
	... default css ...
	}
the visited state
This is addressed using the :visited pseudo-class. So:
#homeButton:visited {
	... visited css ...
	}
the rollover state

This is the most complex to address - we need :hover and :focus first of all, but also the visited state of :hover (for cross-browser stability); then we need to add the default and visited state of the script's internal rollover class, which is called "udmR". (The rollover class is used to maintain persistence - so that when you move to a menu its parent link remains highlighted, which wouldn't happen with pseudo-classes alone).

Finally we'll add :active, not actually for a different active state, but to simulate :focus in Win/IE (it doesn't support :focus but it does, incorrectly, apply :active to links when they have the focus).

So:

#homeButton:hover, #homeButton:focus, #homeButton:visited:hover, #homeButton.udmR, #homeButton.udmR:visited, #homeButton:active {
	... rollover css ...
	}

And that covers everything - copy of each set of rules for each of your navbar links, then apply whatever styling you want. You can see a simple example of this in the Individual styles demo.

If you find that any styles you're applying don't seem to have any effect, it's most likely insufficient specificity - ensure that these rules come after the menu stylesheet, or if that isn't enough, use !important. You can also use !important if you want to force all states of a link to have the same property, to save defining multiple rules:

#homeButton {
	property:value !important;
	}

You can use !important as much as you like without concern - because user !important rules always take precedence.

Individual item widths

The links in a horizontal navbar normally have "auto" width, but if you're making a pixel-precise layout then you might want to set them exactly.

It should be as simple as applying width to each link in the navbar, but in Opera 7.0 to 7.2 applying a fixed width to floated and positioned elements is problematic - the navbar might collapse, such that the links are all on top of each other. To avoid that we have to apply widths to the list-items as well as to the links; we can't apply them just to list-items, or it won't work in Opera 5. Phew!

So, we need an id on each list-item and link in the navbar:

<ul id="udm" class="udm">
	<li id="home"><a id="homeButton" href="/">Home</a>
	</li>
	<li id="about"><a id="aboutButton" href="/menu/">About</a>
	</li>
	<li id="contact"><a id="contactButton" href="/contact/">Contact</a>
	</li>
</ul>

Then we can set the widths like this:

#home, #homeButton, #homeButton:visited {
	width:50px;
	}
#about, #aboutButton, #aboutButton:visited {
	width:70px;
	}
#contact, #contactButton, #contactButton:visited {
	width:90px;
	}

However that's still an over-simplified example. In practise the links might have padding, margins or borders, so we might have to think about box-model differences; say, for example, the links have 2px borders, 10px left/right padding and no margins, there would be a total discrepancy of 24px.

One way to deal with that is have IE6+ in Standards-compliant (sic) mode, and use the simplified box model hack to feed different widths to IE5:

#home {
	width:74px;
	}
#homeButton, #homeButton:visited {
	width:74px;
	w\idth:50px;
	}

#about {
	width:94px;
	}
#aboutButton, #aboutButton:visited {
	width:94px;
	w\idth:70px;
	}

#contact {
	width:114px;
	}
#contactButton, #contactButton:visited {
	width:114px;
	w\idth:90px;
	}

But Konqueror 3.1 or earlier doesn't get past that hack, so an alternative is to force IE6 into quirks mode and use the 'star html' selector to feed different widths to IE5-6, contained within @media so that Mac/IE5 doesn't see them:

#home  {
	width:74px;
	}
#homeButton, #homeButton:visited {
	width:50px;
	}
@media screen,projection {
	* html #homeButton, * html #homeButton:visited {
		width:74px;
		}
	}

#about {
	width:94px;
	}
#aboutButton, #aboutButton:visited {
	width:70px;
	}
@media screen,projection {
	* html #aboutButton, * html #aboutButton:visited {
		width:94px;
		}
	}

#contact {
	width:114px;
	}
#contactButton, #contactButton:visited {
	width:90px;
	}
@media screen,projection {
	* html #contactButton, * html #contactButton:visited {
		width:114px;
		}
	}

Of course it's easier if the links are all the same width - then you'd only need one set of rules; you can see an example of that in the Chocolate Bar demo. But either way - once you have one set of rules, it's fairly simple to copy and edit them for further items; you can reduce the verbosity by compacting each rule to a single line.

Making an image-based navbar

Now you've seen the basic techniques for styling the navbar externally and then applying individual item widths, let's move onto a more detailed example, which uses these techniques to make an entirely image-based navbar.

Adding the link attributes

For this we need not just id-selectors, but also a generic class-selector. We also need an extra <span> inside each of the links, which will be used to do image-replacement - the <span> should come before the link text, or it won't work correctly in Mac/IE5.

So we begin with a structure like this:

<ul id="udm" class="udm">
	<li id="home"><a id="homeButton" class="navButton"
		href="/"><span></span>Home</a>
	</li>
	<li id="about"><a id="aboutButton" class="navButton"
		href="/menu/"><span></span>About</a>
	</li>
	<li id="contact"><a id="contactButton" class="navButton"
		href="/contact/"><span></span>Contact</a>
	</li>
</ul>
Disabling navbar menu indicators

Next an important consideration - we'll be applying styles to <span> elements which are descendents of navbar links, but navbar menu indicators are also <span> elements and descendents of navbar links; so to prevent the image-replacement also applying to them, before going any further you must disable navbar menu indicators, by setting that value to "none" - you can get the same effect by adding arrows to the images you use.

This does mean that menu indicators won't be present when images are turned off, but you can make up for that by adding some indicator-text directly into the relevant links:

<li id="about"><a id="aboutButton" class="navButton"
	href="/menu/"><span></span>About ..</a>
</li>

Submenu indicators are unaffected - you can use them as normal.

Making the images

Next we need some images, and we can put both the default and rollover states in the same actual image, then use background-position to change between them - I got this idea from Petr Stanicek [aka -pixy-].

Here's a sample image:

Image showing the word 'Home' in a box with a pale-blue background; below that is a space, and then a similar box again but with a darker, more purple background

The great advantage of this is that no pre-loading is required - the rollover image will be instantly there.

The one disadvantage is it exposes the "image-flickering" bug in IE6, but there are a number of possible solutions to this, discussed in the article: Flickering images in Internet Explorer 6

Writing the CSS

Next we define the visible image size, as dimensions of the navbar links. To avoid box-model differences first set the padding, margin and border-size of navbar links to "0" from the navbar item styles array, and make sure the border-collapse is set to "separate".

If you're making a vertical navbar then the images will probably be all the same size:

/* link dimensions correspond with image dimensions */
.navButton, .navButton:visited {
	width:126px;
	height:36px;
	position:relative !important;
	}

(The additon of position:relative is for Mac/IE5 whose links might otherwise be position:static ... the why of that is complicated ... please take my word for it :))

If you're making a horizontal navbar then the images will probably be different widths, so you'll need to define them individually. We've already seen how to apply individual item widths, and this is the same as the simplest example:

/* link heights corresponds with image height */
.navButton, .navButton:visited {
	height:36px;
	position:relative !important;
	}

/* link and list-item widths correspond with image widths */
#home, #homeButton, #homeButton:visited {
	width:69px;
	}
#about, #aboutButton, #aboutButton:visited {
	width:75px;
	}
#contact, #contactButton, #contactButton:visited {
	width:92px;
	}

Next we style the <span> elements that are used for adding the images:

/* span is same dimensions as link and positioned to superimpose */
.navButton span, .navButton:visited span {
	display:block;
	height:36px;
	width:100%;
	position:absolute;
	left:0;
	top:0;
	z-index:1;
	background-repeat:no-repeat;
	}

The positioning on the <span> will place it exactly on top of its link - the text is still there, but covered by the image. The z-index is necessary for Opera 5 and 6, to prevent the text and image both showing up at the same time.

You must not attempt to hide the underlying text, because you'll be hiding it from screenreaders as well, and also from browsers with CSS turned on but images turned off.

Each image is a different <span> background, so let's define those next:

/* button images are span background images */
#homeButton span, #homeButton:visited span {
	background-image:url(/udm-resources/button-home.gif);
	}

#aboutButton span, #aboutButton:visited span {
	background-image:url(/udm-resources/button-about.gif);
	}

#contactButton span, #contactButton:visited span {
	background-image:url(/udm-resources/button-contact.gif);
	}

By using background-image rather than the shortcut-property background, we ensure that a redundent background-color can still apply. When images are not available the links underneath will be visible, and you can style them in the normal way using the navbar item styles array.

Finally it just remains to implement the rollovers, which is done with background-position. We've seen what rules are necessary to address the rollover state, and this is just the same:

/* rollovers are background position so that no preloading is necessary */
.navButton span, .navButton:visited span {
	background-position:0 0;
	}

.navButton:hover span, .navButton:focus span, .navButton:visited:hover span, .navButton.udmR span, .navButton.udmR:visited span, .navButton:active span {
	background-position:0 -100px;
	}

And there you go :) Have a look at the demos to see this in action: vertical image-based navbar demo and horizontal image-based navbar demo.

Notes on image replacement

The technique I've used here is one improvement on conventional FIR, in that it works when CSS is on but images are off. And since none of the text is 'hidden' or non-displayed in any way, screenreaders won't have a problem reading it.

However it does require an extraneous <span> inside the link, and also has its own potential accessibility issue - the link dimensions are fixed in pixels, so if the text is scaled to a very large size in a browser with CSS on but images off, it may begin to disappear behind the border.

If that turns you off you may be interested in GIR - an alternative technique that doesn't have these issues, although it does rely on javascript and doesn't work fully in Mac/IE5.

For a discussion of image-replacement techniques in general, check out Using background-image to replace text. For some beautiful examples of image-replacement in action I recommend the CSS Zen Garden.

Styling the menus externally

Although the styling available through the main configuration file is already comprehensive, there may be times when it's too comprehensive - if you just want to make small adjustments, it might seem excessive to define a whole new menu class or menu-item class.

So we can address the menus from external CSS, just as we addressed the navbar. Here are some ideas:

Menu widths

Many of the individual menus on this site have different widths. I've done that with <ul> style attributes:

<ul style="width:8em">

You could add an id or class name, and use external CSS, but you'll probably need to add !important to ensure the rule has enough specificity.

Menu icons

The main menus on this site have icons next to some of the links. This is acheived by creating a space with padding-left, then adding a non-tiled background-image from external CSS.

So first the padding, and we can use the menu-item additional CSS to set that from the configuration file:

	'padding-left:29px;',  // additional link CSS (careful!)
	'padding-left:29px;',  // additional hover/focus CSS (careful!)
	'',                    // additional visited CSS (careful!)

Then we can use multiple class names on the links, to specify which should have icons and which icon they should have. For example these class names:

<a href="/menu/benefits/" class="icon thumbsup">
	The Benefits of UDM 4</a>

<a href="/menu/modules/" class="icon cog">
	Modules &amp; Extensions</a>

go with CSS like this:

#udm a.icon {
	background-repeat:no-repeat;
	background-position:2px 2px;
	}

#udm a.thumbsup {
	background-image:url(/images/thumbsup.gif) !important;
	}
#udm a.cog {
	background-image:url(/images/cog.gif) !important;
	}

Here the use of !important is simply to avoid having to define separate rollover styles; but you can have rollover icons as well if you like, just by defining the extra pseudo-classes:

#udm a.thumbsup {
	background-image:url(/images/thumbsup.gif);
	}
#udm a.thumbsup:hover, #udm a.thumbsup:focus, #udm a.thumbsup:active {
	background-image:url(/images/thumbsup-rollover.gif);
	}

#udm a.cog {
	background-image:url(/images/cog.gif);
	}
#udm a.cog:hover, #udm a.cog:focus, #udm a.cog:active {
	background-image:url(/images/cog-rollover.gif);
	}

It's also a good idea to cache the icons, so you don't get a loading delay when first opening their menus:

//cache menu icons
var icons = [
	'thumbsup',
	'thumbsup-rollover',
	'cog',
	'cog-rollover'
	];

var iconsLen = icons.length;
var imgs = [];

for(var i=0; i < iconsLen; i++)
{
	imgs[i] = new Image;
	imgs[i].src = '/images/' + icons[i] + '.gif';
}

You can see a nice example of this in the Office XP demo.

Dividers and decorations

Since the links are styled while the list-items are plain, we can use list-item margin to create spaces between adjacent links. Or we could use a combination of margin and padding to place a border halfway between two links, and that's what I'm going to show you here. You can see it in use with the menus on this site - many of them have dividers like these:

/* menu dividers */
.udm li.dividerBelow {
	margin-bottom:2px !important;
	padding-bottom:2px;
	border-bottom:2px solid #ecefc6;
	}
.udm li.dividerAbove {
	margin-top:2px !important;
	padding-top:2px;
	border-top:2px solid #ecefc6;
	}

The margin properties need !important so that they take precedence over the margins that are already defined by the script. These styles will cascade, so every list-item in a descendent menu would have the same divider. That's not really desirable, so I've used them only on items which don't have a menu - having Below and Above variations gives flexibility in where the class can be placed, for example:

<li class="dividerBelow"><a href="/products/">Products</a></li>
<li><a href="/services/">Services</a>
	<ul>
		...
	</ul>
</li>

or:

<li><a href="/products/">Products</a>
	<ul>
		...
	</ul>
</li>
<li class="dividerAbove"><a href="/services/">Services</a></li>

will place a divider between those two links. If that's not possible you can use an id attribute on a particular list-item, and apply the divider that way.

Transition effects

In addition to global transition effects (for IE5.5 or IE6), you can add individual menu transitions in the same was as we added menu widths - using a style attribute, or a class or id plus external CSS.

For example, class names like these:

<ul class="fade">

<ul class="pixelate">

<ul class="dissolve">

would go with CSS like this:

ul.fade {
	filter:progid:DXImageTransform.Microsoft.Fade(duration=0.3);
	}

ul.pixelate {
	filter:progid:DXImageTransform.Microsoft.Pixelate(duration=0.5);
	}

ul.dissolve {
	filter:progid:DXImageTransform.Microsoft.RandomDissolve(duration=1);
	}

Transition effects work in IE5.5 or IE6 (they work erratically in IE7, and are phased-out completely by IE8). For a list of available effects please see the MSDN Transitions Reference. Filters and transition effects are generated with DirectX, which in Internet Explorer is an ActiveX control (at the same security level as plugins like Flash), therefore they won't work for anyone who has Active-X disabled completely.

They can be quite annoying, and might also be very slow for people on older machines or with low amounts of RAM.

As an alternative to proprietary filters, you can have cross-browser sliding transition effects using the sliding menus extension, with similar reservations for people using older machines.

Adding pure CSS menus

Until recently, I considered Pure CSS menus to be unimplementable - because li:hover triggers interfere with the menu open and close timers, but there seemed no way to apply those triggers only when scripting is unavailable, because <noscript> in the <head> is not allowed.

Pure CSS menus have extremely poor usability, because of the lack of event-discriminated timers; but for browsers that support them and have scripting enabled, that would be as good as the menus ever get - for everyone, all the time.

But no ... wait a minute

Then suddenly I realised, there is a safe way to implement pure CSS menu triggers after all. It's really obvious actually - we just have to get the script to remove them.

Now, removing rules from an arbitrary style sheet requires DOM2 CSS, which is only properly supported in mozilla browsers. So what we have to do is to put the rules in a separate stylesheet, which the script can identify and remove.

I will re-iterate that I still consider pure CSS menus to have poor usability. Their invention was a major innovation, but as with many such things - although it opens our minds to a new way of doing something, the prototype itself is functionally useless in the real world. I think it's better, overall, if dropdown or flyout menus don't work without javascript.

Anyway that's my opinion. If you want them, here's what you do:

Implementation

You must be using Version 4.4 or later to do this. If you're using an earlier version, please download the latest update first.

Please note, first of all, that implementing pure CSS menus adds some very specific limitations to your menu layout and design. Failing to account of any one of these could make your menus completely unuseable:

  1. All the navbar-to-menu and menu-to-menu offsets must be negative values - that is, they should overlap each other sufficiently to provide a decent level of usability.
  2. You can't use any menu or menu-item classes - all your menus must share the default design scheme.
  3. The entire structure should be small enough that all the menus are fully visible above-the-fold at 800x600 - because menu repositioning will not be available.
  4. All the menus should be the same width - or further child-menus offsets would have to be individually reconciled in additional CSS.

Note also that there won't be any submenu-indicator arrows, because these are added dynamically.

The navbar and menu design still takes place in your udm-custom file as before; but the CSS required to open and position the menus without scripting must be in a style sheet of it's own, with the id "udm-purecss":

<!-- pure CSS menu triggers -->
<link rel="stylesheet" type="text/css" media="screen, projection"
	id="udm-purecss" href="/udm-resources/udm-purecss.css" />

Inside that style sheet, very little code is actually required to activate and position the menus. Here's an example from the pure CSS menus demo on this site:

/* menu triggers */
ul[id="udm"] li > ul { visibility:visible; display:block; left:-10000px; overflow:visible !important; }
ul[id="udm"] li:hover > ul { left:auto; height:auto; }

/* submenu offset */
#udm li ul {
	margin:-2.65em 0 0 6.5em;
	}

/* child-menu offset */
#udm li ul ul {
	margin:-2.2em 0 0 8.7em;
	}

The margin values in that example include the necessary overlap I mentioned earlier. The margins obviously vary from submenu to child-menu; as they will with different orientations - a vertical-left, vertical-right or horizontal navbar all require different margins for their menus.

You can download a basic style sheet for each orientation using the links below:

They'll work in the following browser builds: Opera 7.5+, Mozilla 1.1+, Safari 1.2+, Konqueror 3.2+, Internet Explorer 7+.

Print-media CSS

The script-generated stylesheet outputs only to "screen" and "projection" media. This means that, by default, the navigation list will be printed but not print-styled.

It's not practical to make the navbar and menus appear the same to print as they do to screen - it would take more code than it's worth, and since navigational elements are of little or no value to the printed page anyway, I'd recommend simply hiding the navigation, so it doesn't print at all.

You can do this using print media CSS in a separate style sheet or <style> block:

<style type="text/css" media="print">

	/* hide the navbar */
	.udm { display:none; }

</style>

Aural CSS

Practical implementations of aural CSS are rare - in fact there is only one graphical browser that supports it, namely Opera (Version 8 or later).

My knowledge of the subject is in its infancy; all I've done so far is add gender to different elements - the content is male and the navigation female:

html, body {
	voice-family:male;
	}

#udm {
	voice-family:female;
	}

The style sheet is included using a <link> element which has no media attribute, because Opera considers aural CSS part of "screen" rather than "aural" media. This may change in the future, so omitting the media attribute (or setting it to "all") is the safest thing to do.

I'll write more as I learn more. For now, I refer you to some useful resources on the subject:


Search

We would like your feedback! Take the UDM4 Survey!

UDM 4 is valid XHTML, and in our judgement, meets the criteria for WAI Triple-A conformance.

-