Developer

Add clickable icons to your links

Need to find a CSS way to add clickable icons next to a list of links? Builder.com columnist Michael Meadhra shares a simple technique to make it work.

This article originally appeared in the Design & Usability Tactics newsletter. Click here to subscribe automatically.

When you're working with CSS, sometimes the obvious technique doesn't produce the effect you might expect or desire. Fortunately, there's a good chance that you can achieve the effect you want if you're willing to look beyond the obvious and try other methods. I encountered this exact situation recently when looking for a way to add an icon beside each item in a list of links, as shown in Figure A.

Problems with the obvious solution

Since the links are normally marked up as an unordered list, it seems obvious to replace the default bullet preceding each list item with an image file for the desired icon. All you need to do is create a style to specify the list-style-image attribute. If each list item contains a separate id, you can create a separate style for each id so that each item has a different icon.

The technique is simple, and it sort of works, as you can see in Figure B. There are some alignment issues if the icon is taller than the text (as in this example), but the real problem is that the icon isn't a clickable part of the link because the bullet is in a separate box from the list item's text.

Most visitors wouldn't notice whether the bullets beside a list of links are clickable, but icons are different. The icons look a lot like buttons, so visitors will probably try to click them; therefore, it will confuse and disappoint users if clicking the icon doesn't work just like clicking the text portion of the link.

After spending time trying to extend the clickable area of the link over the bullet/icon, I became frustrated with the list-style attributes' limitations and started looking for alternatives. I found some references to creating bullets with more flexible positioning options using display marker. However, since the dominant browser doesn't support that particular part of the CSS specification, it's not a practical solution. (Maybe someday we'll be able to use all of the tools CSS provides, but that day isn't here yet.)

An alternative that works

When the obvious solution to a problem doesn't work, it's time to look for other approaches. An article by Dan Cederholm of Simple Bits provided me with an alternative approach to this particular challenge.

His technique puts the icon into the background image of each list item instead of adding the icons as bullets. It's basically the same technique that is used for creating hybrid graphical/text buttons that employ CSS-styled text over a background image of a button.

The difference is that the icon image doesn't fill the entire background of the list item. Instead, it sits at the left edge of the list item's box, and a padding attribute pushes the text of the list items over to the right, past the icon image. The effect is visually the same as the icons floating to the left of the list items, but the icons are within each list item's box, and you can use display:block to make the entire box clickable. This solves the problem of the unclickable icon.

The following code creates the effect that you see in Figure A. The markup is simply an unordered list of links, each with a separate id attribute.

<div id="linklist">
    <ul>
        <li id="check"><a href="link1A.html">Link 1</a></li>
        <li id="asterisk"><a href="link2A.html">Link 2</a></li>
        <li id="x"><a href="link3A.html">Link 3</a></li>
    </ul>
</div>

Here's the CSS that makes it work:

div#linklist {
    margin-left: 50px;
}
div#linklist ul {
    margin:0;
    padding:0;
    list-style-type: none;
}
div#linklist li {
    height:38px;
    width:100px;
    background-position:left;
    background-repeat:no-repeat;
    line-height: 38px;
    margin-bottom:10px;
}
div#linklist li a {
    height: 100%;
    width: 100%;
    display: block;
    padding-left: 45px;
    text-decoration: none;
}
div#linklist li a:link {
    font-weight: bold;
    color: #000000;
}
div#linklist li a:visited {
    font-weight: normal;
    color: #999999;
}
div#linklist li a:hover {
    font-weight: bold;
    color: #0000FF;
}
div#linklist li a:active {
    font-weight: bold;
    color: #CC0033;
}
div#linklist li#asterisk {
    background-image:url(asterisk.jpg);
}
div#linklist li#check {
    background-image:url(check.jpg);
}
div#linklist li#x {
    background-image:url(x.jpg);
}

Deconstructing the code

The div#linklist ul style zeros out the default margins and removes the default bullets normally associated with an unordered list. The div#linklist li style defines the size of the list item box (height:38px and width:100px), positions the background image at the left end of that box (background-position:left;), and sets the background image to show only one copy of the image (background-repeat:no-repeat;). In addition, the line-height:38px rule centers the text vertically within the box, and the margin-bottom:10px rule adds a little space between icons.

The div#linklist li a style makes the entire box clickable (height:100%; width:100%; display:block;) and removes the underline from the links (text-decoration:none;). This is also the home of the padding-left:45px rule, which pushes the text over to the right of the icon. It's important to note that the padding-left rule is part of the style for the div#linklist li a selector instead of the div#linklist li selector. Otherwise, text within the <a> tag would revert to its normal position.

The styles for the various link pseudoclasses (div#linklist li a:link, etc.) set the text formatting for the corresponding link states. This is the normal text rollover effect and doesn't really affect icon placement or clickability.

Finally, the styles for each of the named list items (div#linklist li#check, etc.) set the background images to use as icons for the corresponding links. If you want to use the same icon for all the list items, you could put the background-image:url(check.jpg); rule in the div#linklist li style instead of creating separate styles for each item.

Note: For the sake of clarity, this example code uses long selectors to produce maximum specificity. In most cases, you could shorten a selector such as div#linklist li#check to become #check. The resulting style sheet would be smaller and work just as well.

Editor's Picks