Fun with links
Yesterday we talked about the box model. Today we’re going to put a small part of it to work, by investigating ways of styling links. Before getting stuck in, we need to talk a bit about pseudo-selectors (also known as pseudo classes). The CSS specification defines pseudo classes as “characteristics that cannot be deduced from the document tree”, but in practise the only widely implemented psuedo selectors are those that apply to links. The key psuedo selectors for links are:
a:linkapplies to normal links (but not to<a name="">elements)a:visitedapplies to visited linksa:activeapplies to active links (links that are currently being clicked on)a:hoverapplies to links that the mouse is currently positioned over
It is the last of these that we shall be focusing on. One of the first and most popular effects that emerged when Javascript became popular was the image rollover, where an image (generally used as part of a site’s navigation) would change when the mouse was positioned over it. a:hover allows us to achieve a similar effect using just CSS. The most striking change is to alter the background colour, as shown in this example.
After yesterday’s discussion of the box model, it seems we would be able to achieve a lot more with this effect if links were block level elements. We can use the display: block; property to “promote” a link to block level status, which allows us to achieve all kinds of groovy effects. Consider the following HTML:
<div id="menu">
<a href="/">Link 1</a>
<a href="/">Link 2</a>
<a href="/">Link 3</a>
<a href="/">Link 4</a>
</div>
Left to its own devices, this would display as a rather uninteresting line of links. Add some CSS and we can make it look much more interesting:
div#menu {
width: 5em;
border: 1px solid red;
border-bottom: none;
}
div#menu a {
display: block;
text-decoration: none;
border-bottom: 1px solid red;
background-color: white;
color: red;
padding: 0 0.2em;
}
div#menu a:hover {
background-color: red;
color: white;
}
A few things to note. Most importantly, the links inside the div are set to display: block. This causes them to “stack up” on top of each other, and also causes them to expand to be as wide as their containing box (which is why we have restricted its width to 5 ems). The menu border is set to 1px all the way round, but then the bottom border is turned off. This means that when we set a bottom border on the links themselves the very bottom link does not end up with a double border. The result of the above can be seen here.
How about horizontal navigation? That can be achieved in a similar fashion, using the float property to position the links next to each other:
div#menu a {
display: block;
float: left;
width: 5em;
padding: 0 0.2em;
margin-right: 1em;
text-decoration: none;
border: 1px solid red;
background-color: white;
color: red;
}
div#menu a:hover {
background-color: red;
color: white;
}
Check out the result here. Now that the links are floated it is important they have an explicit width assigned to them. A margin-right is used to place a gap between the links. There is one caveat to this method: It is important that any following element (preferably one that spans the whole page) as clear: both; applied to it, or any text in that element will flow around the menu rather than appearing below it. This is one of the most common upsets caused by using floats, and exactly the thing the clear property is designed to solve.
There is one problem with the technique described so far: accessibility. If you run the above examples through any accessibility checker they will flag up a warning that the links in the examples are seperated only by whitespace. Try disabling CSS or viewing the examples in a non-CSS browser and you’ll see that this could definitely cause confusion—the links are displayed on a single line with only a single space between each one. A solution is at hand in the form of the humble <span> element accompanied by the display: none property:
div#menu span {
display: none;
}
<div id="menu">
<a href="/">Link 1</a><span> | </span>
<a href="/">Link 2</a><span> | </span>
<a href="/">Link 3</a><span> | </span>
<a href="/">Link 4</a>
</div>
Here’s a demonstration of the above. Note that it’s identical to the previous example in CSS browsers; the separating pipes only show up when the CSS is not applied.
Even though many people think they know CSS, when you step through a simple process methodically, extra nuggets of knowledge present themselves.
I found two such "Aha!" moments from your methodical approach:
clear: both;for followingfloatspanwithdisplayset tonone, to appease the accessibility requirementsAdam - 28th May 2003 02:52 - #
I've really been enjoying your tutorials. A really great contribution.
One point I'd make on this latest example is that it'd be even nicer to add id="menu" to an unordered list. It's just as easy to style, yet degrades better. Also, others have pointed out that pipes separating navigation items get spoken out each time when read by a screen reader.
Check out this post on the subject: http://www.stopdesign.com/log/default.asp?date=200 30123
More importantly though, keep up the great work!
Dan Cederholm - 28th May 2003 04:06 - #
Anne van Kesteren - 28th May 2003 06:19 - #
Please note that when you apply block display to links, Opera 6.x (and below, I think) has a bug where the underline appears 1em too high (so it appears as an overline).
Jim - 28th May 2003 09:16 - #
Tom - 28th May 2003 10:38 - #
w - 28th May 2003 17:26 - #
Just to clarify,
a:linkapplies toAelements that have anHREFattribute, regardless of whether they've aNAMEorIDspecified.kevin c smith - 28th May 2003 17:32 - #
Graham - 28th May 2003 19:46 - #
Simon Willison - 28th May 2003 21:26 - #
Jun Fonte - 29th May 2003 00:26 - #
Read about you on Eric Meyer's blog and thought I'd visit. You do a good job of displaying things in bite sized pieces.
alanjstr - 29th May 2003 14:35 - #
Simon, I think you need to read http://dbaron.org/css/1999/09/links and then rewrite this blog entry. ;-)
Ian Hickson - 29th May 2003 16:27 - #
Markku Seguerra - 30th May 2003 18:54 - #
Greg Thorne - 30th May 2003 21:02 - #
On a pretty simplistic level, DIV is meant for marking up pieces (chunks/divisions) of code or text, while SPAN is meant for marking phrases, usually within text paragraphs or sentences. For example:
(I'm not sure how Simon handles code snippets in comments, so I'm using -P- for paragraph tags, and so on. I will write a proper xhtml version of this.)
-DIV class="Examples"--P-This is a paragraph of text. To mark up a book title, use the span tag. For example, -SPAN class="book title"-Good Omens-/SPAN- by Neil Gaiman and Terry Pratchet.-P-This is another paragraph, just to show that it's appropriate to have multiple paragraphs, and other block elements, within a single div tag.-P--/DIV-*crosses fingers, hopes this is rendered as hoped, and wishes Simon W had a Preview button*
Micah - 30th May 2003 22:28 - #
re: Tom's question about the hover target not spanning the whole box in block mode on IE6, this is answered in "A List Apart - Taming Lists".
If you put the addition of 'width : 100%;' in the 'div#menu a' selector then IE6 should hover correctly across the whole box. An additional rule should be added immediately following this to keep other browsers working, with the child selector of 'html>body #menu a' and the element of 'width : auto;'.
And Simon, nice series of articles on CSS. Very helpful.
William Ryken - 30th May 2003 23:04 - #
Micah, your example is slightly unclear. class="book title" actually indicates that the element has two classes, 'book' and 'title'. So either of the selectors book { ... } and title { ... } will match that particular span element.
Jim - 31st May 2003 13:08 - #
Ah, sorry if that was unclear. I usually mark up titles with two classes in case I want to, for example, make all titles display in italics and all book titles with small book icons afterwards, movie titles with movie-screen icons, newspaper titles with little news icons and so on.
.title { font-style: italic; }.book:after { content: url("minibook.gif"); }.movie:after { content: url("miniscreen.gif"); }...Not that I've gotten around to implementing that, but maybe someday. I'm more of a theorist.
Micah - 31st May 2003 23:16 - #
Cheri - 22nd October 2003 03:16 - #
Chuck - 24th February 2004 20:10 - #