Blog

Accessible CSS roll over images

While some might claim that with the increase in typography control in css, and the number of people browsing with anti-aliased fonts(not that many, thanks microsoft! *cough* cleartype *cough*) that one no longer needs roll-over images. The argument being that they require a preloader, increase download times, and they are really not that accessible. I disagree however –

By mixing a few css techniques we can obtain fully accessible roll over images with out a whole lot of extra code or require extra javascript/dom.

We will start with the HTML so we can build the basic structure and totally separate design from content. Our goal is to have accessible mark-up, so we want our menu of roll over images to be a list, and have a text link for alternative needs (screen readers, text browsers, handhelds, etc.)


<div id="menu">
     <ul>
          <li><a href="index.html">Front Page</a></li>
     </ul>
</div>

Now we have a menu that is a list, and it has a text link so it is accessible. We do want it to look visually pleasing however, and have a roll-over effect. We also don’t need the bullet that a list, nor the margins. So into CSS we dive to remove the list styling and add some style of our own!


#menu ul {
     display: inline;
     margin: 0;
     padding: 0;
}

#menu li {
     display: inline;
     list-style: none;
     margin: 0;
     padding: 0;
}

The next step is designing the roll over image itself. I went ahead and designed an image in three different states (roll over, active, and visited) as seen here:

Front Page Active Button

Front Page Visited Button

Front Page Rolled Over

While we could simply swap the background on the different events in CSS, we would have to preload each image, and it still gets a flickering effect in IE. The solution is simple, browsers render the movement of a background image quicker than swapping one. So I created an image with all three states together plus a default state for the image.

Roll Over to see it hereFront Page Button, Combined

Each rendition of the button is 116 pixels wide, so we will set the width of our button to 116 px, set the background to our roll over image, and it will only show the first 116 pixels (or our first button.) When we want to change which rendition it is showing, we will use the background-position: tag in css to shift it over 116 or more pixels.

To start this, we will create a class for a li element:


li.frontpage a{
     background: url((images/fp.gif);
     display: block;
     width: 116px;
     height: 17px;
}

Now we can change our HTML to:


<div id="menu">
     <ul>
          <li class="frontpage" ><a href="index.html">Front Page</a></li>
     </ul>
</div>

The li element is now a block, and has the height/width and background we assigned it. Now all it needs is some basic functionality.

Back into CSS, lets define some states for hovering, and visited links.


li.frontpage a {
     background: url(images/fp.gif);
     background-position: 0 0;
     display: block;
     width: 116px;
     height: 17px;
}


li.frontpage a:hover {
     background: url(images/fp.gif);
     background-position:
     -116px 0;
}

li.frontpage a:visited {
     background: url(images/fp.gif);
     background-position: -348px 0;
}

li.frontpage a:visited:hover {
     background: url(images/fp.gif);
     background-position: -116px 0;
}

This will shift the background image to the left in 116 pixel increments, resulting in a roll-over effect.

We still have the text link over the image however - which looks pretty crappy. Lets get that out of there while still ensuring that screen readers, text browsers, and hand helds can see it. This will be done using Mike Rundle's Image Replacement technique.

We change our li.frontpage a tag to the following...


li.frontpage a {
     background: url(images/fp.gif);
     background-position: 0 0;
     width: 116px;
     height: 17px;
     text-indent: -999999px;
     overflow: hidden;
}

The text-indent pushes the text out of the field, and by designating a hidden overflow it is not rendered on the screen. Text browsers, screen readers, handhelds, and those browsing with out style sheets will see the text links however and it remains fully accessible!

Here is the end result!

It was pointed out on digg that if the images don't load (or are turned off, but css is on) then you get nothing. This is true, and some people probably still browse with css on but images off... so here is the suggested alternative.


<li class="replace" id="frontpage"><a href="index.php">Front Page<span></span></a></li>

The CSS:


.replace{
     position:relative;
     margin:0px;
     padding:0px;
     /* hide overflow:hidden from IE5/Mac */
     /* \*/ overflow: hidden; /* */
}

.replace span{
     display:block;
     position:absolute;
     top:0px;
     left:0px;
     z-index:1; /*for Opera 5 and 6*/ 
}

#frontpage a, #frontpage a span{
     height:17px;
     width:116px;
     background-image: url(images/fp.gif);
     background-position: 0 0;
}