Feed Sign in with OpenID OpenID

Simon Willison’s Weblog

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:link applies to normal links (but not to <a name=""> elements)
  • a:visited applies to visited links
  • a:active applies to active links (links that are currently being clicked on)
  • a:hover applies 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.

This is Fun with links by Simon Willison, posted on 27th May 2003.

Tagged ,

View blog reactions

Next: Golden Mean

Previous: Understanding the Box Model

20 comments

  1. 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:

    1. the purpose of the clear: both; for following float
    2. the addition of a span with display set to none, to appease the accessibility requirements

    Adam - 28th May 2003 02:52 - #

  2. 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 - #

  3. Why o why didn't you use lists? Ala : Taming Lists.

    Anne van Kesteren - 28th May 2003 06:19 - #

  4. 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 - #

  5. Does anyone know what causes IE 6 to use the whole box as a hover target on the floated version, but not on the block version?

    Tom - 28th May 2003 10:38 - #

  6. the

    w - 28th May 2003 17:26 - #

  7. Just to clarify, a:link applies to A elements that have an HREF attribute, regardless of whether they've a NAME or ID specified.

    kevin c smith - 28th May 2003 17:32 - #

  8. I'm still not sure why the block element has to be defined. What does it do, and how is it different from inline?

    Graham - 28th May 2003 19:46 - #

  9. I talked about the different between block- and inline-level elements yesterday. Basically, block level elements can have padding and margins assigned to them and always appear on a new line, while inline elements are things like links that don't cause newlines to appear.

    Simon Willison - 28th May 2003 21:26 - #

  10. I used it in my website for my menu but a little different thru experiments it renders nicely in gecko based browser and opera but not in IE. Check my website. Great tutorial.

    Jun Fonte - 29th May 2003 00:26 - #

  11. 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 - #

  12. 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 - #

  13. Nice tutorial. Just one question: isn't this method adding unnecessary bits of code to the markup? Wouldn't it be nicer to put the links as members of an unordered list, producing a more "structural" listing of the sections/links?

    Markku Seguerra - 30th May 2003 18:54 - #

  14. Great tutorial, I'm really enjoying it. One thing that's probably painfully obvious to the CSS masters out there, but not yet clear to me is the difference between the <span> and <div> tags, and where each should be used. Could someone shed some light on this for me?

    Greg Thorne - 30th May 2003 21:02 - #

  15. 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 - #

  16. 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 - #

  17. 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 - #

  18. 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 - #

  19. I found your link just cuz I was searching for blogs (never looked at one before) and thought I should look at some design-related ones. THANK you for the time you take to put this info out there. I am learning CSS and there is sOOOOO much I don't get yet! It's very nice to find step-by-step tutorials with examples like this so I can follow it myself. And other peoples' comments are very helpful - especially those who experiment with your how-to's and try new ways to do it. Thanks again, I'm a major fan already!

    Cheri - 22nd October 2003 03:16 - #

  20. I'm a newbie when it comes to CSS. I've been building sites for years with simple static HTML but recently have come to realize just how effective CSS would be in my efforts. Here's my problem: I cannot figure out how to create a link in CSS. I have one word I want the human visitors to see and follow, but not the spiders. I don't want to have to learn a new design language to do this either. I have DreamWeaver 4.0 and have been able to do everything with CSS except create a link with it. Any help or directions to an understandable tutorial would be greatly appreciated.

    Chuck - 24th February 2004 20:10 - #

Comments are closed.

Previously hosted at http://simon.incutio.com/archive/2003/05/27/funWithLinks

A django site