Tutorials Bugs Masterclass Free stuff Test pages Proposals

RichInStyle.com - Style sheet master class: part 8 - links

Contents

Introduction

CSS issues

Pseudo-classes

Order of pseudo-classes

Browser bugs

:active and :hover

:link and :visited - Opera, IE 3 and Netscape

Very serious bugs

Less serious bugs

Conclusion

An alternative option

Introduction

For an area of CSS that is so heavily used, it is sad that there is such lamentable support in almost every web browser. This guide is designed to present two things: firstly, a guide to link styling with CSS, and secondly the best way to avoid bugs in browsers.

CSS issues

Pseudo-classes

CSS provides five pseudo-classes that are pertinent to the styling of links:

:link
Applies to unvisited links
:visited
Applies to visited links
:active
Applies to elements that are being activated (BUTTON, INPUT, A, etc.)
:hover
Applies to elements that are being hovered over.
:focus
Applies to elements that have the focus

As you can see, every possible state of a link is described. There are a number of noteworthy points relating to them. The main one is that of the five, only two are exclusive to links. This means that although it is perfectly safe to have :link {color: red; background: white}, it must be considered unsafe to have :hover {text-decoration: underline}, since although this would only be applied to anchor elements in Internet Explorer 4 or 5, in a more compliant browser such as Netscape 6, it would result in any element over which the mouse was moved being underlined. Similar rules hold true for :active and :focus. As a result, with these pseudo-classes you should include the element_name (e.g., A:active {color: red; background: white} rather than merely :active {color: red; background: white}).

This will, of course, limit you to links created by the A element (and thus by implication to the JavaScript link() method), but this doesn't matter, except under the highly unlikely event that you create a link element using XML.

Since it is essential to include the A with three of the pseudo-classes, I strongly recommend that you do so with the other two as well for reasons of consistency (and also because A:hover overrides :link since A:hover has greater specificity). Thus:

A:link {}

A:visited {}

A:active {}

A:focus {}

A:hover {}

Finally, there is the simple A selector (e.g., A {}). This has the advantage of simplicity - it applies absolutely and unequivocally to A - there is no chance whatsoever of confusion. In itself, it is entirely unproblematic; however, when used in addition to rulesets referenced by pseudo-classes, certain browsers suffer problems (detailed below).

The principal disadavantage that it has is that it applies to A[name] just as much as to A[href]. However, if you adopt best practice, this will not be a problem, as A[name] elements should always be 'empty'. To illustrate, bad is:

<P>
<A name="whatever">Some text</A>
</P>

Better (and equivalent) is:

<P>
<A name="whatever"></A>
Some text
</P>

But not (A should occur inside a block element):

<A name="whatever"></A>
<P>
Some text
</P>

Order of pseudo-classes

At CSS 1, there was no problem with link styles' order - the three states, :active, :link and :visited were mutually exclusive. At CSS 2, however, while the :link (unvisited) and :visited (visited) pseudo-classes (obviously) remain mutually exclusive, the :focus, :hover and :active pseudo-classes (although it is likely that any real user agent would remove the focus from a link after it has been activated) can theoretically all apply at the same time in addition to the link being visited/unvisited.

As a result, if you just use the basic single pseudo, then you probably won't get the effect that you want. For example:

A:link, A:visited {text-decoration: none}

:hover {text-decoration: underline}

The :hover declaration would never be used for links, since all hovered-over links are both :hover and either unvisited or visited. Since there are conflicting declarations, the cascade is used to determine the result. A:link has specificity greater than :hover, and therefore it is used in preference to it. And:

A:hover {text-decoration: underline}

A:link, A:visited {text-decoration: none}

Here the :hover declaration would also be ignored since A:hover has equal specificity with A:link/visited and therefore the order sort determines the result. The :link and :visited declarations come later in the style sheet, and therefore will be used over the :hover declaration.

CSS 2 provides the perfect solution in the use of multiple pseudo-classes. Thus A:hover:link:focus:active applies only to links that are being activated with the mouse hovering over them that have the focus and are previously unvisited.

Browser bugs

It seems that when the browser makers released their browsers, they were trying to compete to see who could release the browser with the buggiest support for link styling. If they were, it was a hard-fought contest but one that has a clear winner - by a clear margin Opera is the browser that has the worst implementation in any browser. To give just a flavor of the bugs that it has, it has bugs that make links invisible (a problem so serious that all the links have a SPAN inside all of the A elements on RichInStyle.com's front page to override it (and also on the bug table)), bugs that destroy entire documents by enclosing every element in borders or applying margins to every element, etc. In addition to these page-destroying bugs, it manages to only correctly support one property on links.

If you think that sounds bad, then you're right, but Opera is not unique - Internet Explorer 3 and Netscape also have execrable (though nothing page destroying) link styling implementations.

In view of this, I think it will probably be best to start with the two browsers that have reasonable CSS implementations: Internet Explorer 4 and Internet Explorer 5.

Uniquely, these browsers support style perfectly on :link and :visited [subject to a general lack of support; e.g., for borders on inline elements]. The only area in which their support falls down somewhat is in the area of support for :hover and :active. This is most likely due to the fact that IE 5's implementation is no different from IE 4's, and IE 4 was released prior to the release of the CSS 2 specification.

:active and :hover

Both of these browsers destroy A selectors that have multiple pseudo-classes by applying the last one only. As a result of this, I recommend that you hide multiple pseudo-class declarations from IE 4 by use of a method detailed in RichInStyle.com's guide to cross-browser style. This is simply done by including a comment inside the simple selector (IE will ignore such declarations even though comments are valid inside them) - e.g., A/* comment will hide it */:link:active {}. Better still would be to exclude such declarations from your IE style sheet if you are using multiple style sheets, but of course you cannot if you do not.

Having explained that IE doesn't support the specification on :hover and :active, it is probably as well to explain what it does do, and that is this:

  1. If the selector is a hovered-over visited link, then both the :hover and :visited declarations are parsed as if they read :visited:hover. E.g., A:hover {color: red; background: green} A:visited {color: green}, would be parsed as A:hover:visited {color: red; background: green} A:hover:visited {color: green}, which is order sorted down to A:hover:visited {color: green; background: green}
  2. If the selector is a hovered-over unvisited link, :hover is parsed as :hover:link, whereas :link simply as :link. This means that :hover always overrides :link except when they are inconsistent. For example:

    :hover {color: red}
    A:link {color: green; background: red}

    Is parsed as:

    :link:hover {color: red}
    A:link {color: green; background: red}

    Which is the same as (order sort):

    :link:hover {color: red; background: red}

  3. :active is also applied to :focus, which isn't supported
  4. For hovered over links that are being activated, :hover is parsed as :hover:active and :active as is.
  5. For visited or unvisited links that are being activated, :active is parsed as :visited:active/:link:active and :link/:visited as is.

:link and :visited - IE 3, Opera and Netscape

Very serious bugs

The most serious bug in Netscape is the one whereby all declarations on all link pseudo-classes are treated as if they were declared on A instead, except for color, which is correctly honored. For example, :link {font-size: 18px; background: red} :visited {font-size: 8px; background: red} would be parsed as A {font-size: 18px; background: red} A {font-size: 8px; background: green}, and the result would be all links 8 pixels high with a green background (because later declarations override earlier ones). However, :link {color: red} :visited {color: green} is correctly parsed.

This combines with a bug in Netscape that means it gives A greater specificity than A:link and A:visited to mean that I recommend that to style links for a style sheet read by Netscape:

  1. Make all declarations for links, except color, on A
  2. Declare color (and color alone) on :link and :visited

Opera 3.5-3.62 is similarly buggy in that it too only correctly supports color, applying all other declarations on :link, A:link, :visited and A:visited as if they were A, except that it surpasses Netscape in bugginess by applying box model properties (i.e., padding, margin and border) applied to :link to ALL elements - every single one in the document, thereby destroying the page.

The bug in Opera that really takes the biscuit is its habit of ignoring author style sheet settings seemingly at random (i.e., not all of the time), instead applying browser ones. It will even do this if you edit its configuration file and remove the section on link settings.

The significant thing about this is that it will destroy pages as a result - if your background color is one that conflicts with the browser's setting for link color, your links will be unreadable. To make matters worse, it is not possible to predict what settings the user will have for link colors, and so in theory any page is at risk of destruction. However, since Opera has such a tiny share of the market, I only recommend that you test that your background color will look o.k. with its default color (#00f - blue for unvisited, #008 - purple for visited) for the link color.

If you find that a conflict occurs, you should fix it by adding a SPAN inside each and every affected A element. Thus <A href=""><SPAN class="operafix"></SPAN></A>. If you have separate style sheets (see RichInStyle.com's guide to cross-browser style) for each browser, just add SPAN.operafix {color: value} to your Opera style sheet; if not, add HTML SPAN/* comment */.operafix {color: value !important}, which will hide from all browsers other than Opera (and Mozilla, sadly (though the loss is only that of the ability to distinguish between visited and unvisited links)).

With Internet Explorer 3, the main problem is that both :focus and :hover are destroyed. By this I mean that they are ignored, so A:hover is parsed as A, and :hover is parsed as * due to bugs in IE 3. However, since A:link or :link has greater specificity than A, provided you declare these, no problems will be caused.

An additional problem with IE 3 is that it parses :link as :link, :visited. It correctly parses :visited, however, so providing you make a declaration on :visited for every property that you declared on :link, no problems will be caused.

A further problem is related to applying font-size to :visited links. This is inadvisable since IE doesn't reflow subsequent content to the link and therefore it may be overlapped with.

Yet another serious problem is that it doesn't support ids and classes in combination with pseudo-classes (recall, incidentally, that at CSS 1, A.class:link is valid, but A:link.class is not).

Less serious bugs

  1. Netscape fails to mark local hrefs (i.e., those accessed by <A href="#identifier">) as visited - EVER.
  2. Netscape fails to give A:link/visited greater specificity than :link.
  3. Opera frequently fails to mark local hrefs (<A href="#name">) as visited, typically when alt-tabbing across it, on loading a page. However, this is always fixed by a reload.
  4. Netscape, Opera and IE 3 do not support :hover or :active.
  5. IE 3 only applies :visited to local hrefs and to documents visited in a FRAMESET since the last reload.

Conclusion

The number of bugs is rather depressing, but something simple like:

A:link {color: red;
background: transparent /* But not any other color */}
A:visited {color: green;
background: transparent /* But not any other color */}
A:active {color: blue;
background: transparent}
A:hover {color: red;
background: transparent} /* For IE 3 */
A:hover {color: white;
background: black}

will work fine.

An alternative option

At RichInStyle.com, we frequently prefer to style links according to what they do rather than whether they are visited or not. It tends to make for better pages in certain cases. For example, the links at the top of the page are always the same color regardless of whether you've visited them or not. In order to achieve this you might want to try:

A.class {color: }

where the anchor is marked by <A class="class">, or, if you really do prefer different colors for visited and unvisited links, A.class:link and A.class:visited (note that A:link.class won't work).


That's the end of the master class. However, there's plenty more at RichInStyle.com that you will find useful. For example, why not check out the bug table, the bug index, the free stuff (style sheets, MySite, etc.), or the test suite. Alternatively, you can go to the front page by clicking here.