• CSS Image Maps: A Beginner’s Guide

  • By: Jarod Taylor | Category: HTML & CSS
  • While they may not be used as often as they once were in the heyday of table based web design, image maps can still be quite useful in situations that call for it. Unfortunately, for beginners, achieving this with CSS alone, can be a difficult concept to grasp.

    In this tutorial, we're not only going to get you up to speed on how to create a CSS image map, but, we're also going to take it a couple steps further and add hover states to our image map using a CSS background image sprite, as well as a "tooltip" like popup.

    Get SourceView Demo

    Introduction

    In the days of table based web design, using image maps in your web layouts were quite common. They were used to define clickable areas, or "hotspots", within several sliced images (coming together to form one image). During this time, these hotspots were used mostly for navigational purposes. Today, image maps created in CSS are not only a much leaner and cleaner markup, but they're also easily editable and are often used for displaying additional content when hovered, not just as a means of navigation.

    Before we get started, let's take a look at an old school image map using tables. First of all, the majority of interface designers used an image editing program like Fireworks or Photoshop to slice the image into its separate sections, and then exported it into HTML using tables.

    tutorial image

    What you'd end up with is 17 images, a spacer gif, and a mess of table rows and very un-semantical markup.

    <table id="Table_01" width="581" height="271" border="0" cellpadding="0" cellspacing="0">
    	<tr>
    		<td colspan="3" rowspan="2">
    			<a href="http://en.wikipedia.org/wiki/North_America">
    				<img src="images/North-America.png" width="193" height="135" border="0" alt=""></a></td>
    		<td colspan="2">
    			<img src="images/index_02.png" width="21" height="83" alt=""></td>
    		<td colspan="2">
    			<a href="http://en.wikipedia.org/wiki/Europe">
    				<img src="images/Europe.png" width="119" height="83" border="0" alt=""></a></td>
    		<td colspan="2" rowspan="2">
    			<a href="http://en.wikipedia.org/wiki/Asia">
    				<img src="images/Asia.png" width="163" height="135" border="0" alt=""></a></td>
    		<td colspan="2" rowspan="3">
    			<img src="images/index_05.png" width="84" height="165" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="83" alt=""></td>
    	</tr>
    	<tr>
    		<td rowspan="5">
    			<img src="images/index_06.png" width="14" height="187" alt=""></td>
    		<td colspan="2" rowspan="3">
    			<a href="http://en.wikipedia.org/wiki/Africa">
    				<img src="images/Africa.png" width="123" height="138" border="0" alt=""></a></td>
    		<td rowspan="5">
    			<img src="images/index_08.png" width="3" height="187" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="52" alt=""></td>
    	</tr>
    	<tr>
    		<td rowspan="4">
    			<img src="images/index_09.png" width="86" height="135" alt=""></td>
    		<td rowspan="4">
    			<a href="http://en.wikipedia.org/wiki/South_America">
    				<img src="images/South-America.png" width="92" height="135" border="0" alt=""></a></td>
    		<td rowspan="4">
    			<img src="images/index_11.png" width="15" height="135" alt=""></td>
    		<td colspan="2">
    			<img src="images/index_12.png" width="163" height="30" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="30" alt=""></td>
    	</tr>
    	<tr>
    		<td rowspan="3">
    			<img src="images/index_13.png" width="99" height="105" alt=""></td>
    		<td colspan="2" rowspan="2">
    			<a href="http://en.wikipedia.org/wiki/Australia">
    				<img src="images/Australia.png" width="101" height="79" border="0" alt=""></a></td>
    		<td rowspan="3">
    			<img src="images/index_15.png" width="47" height="105" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="56" alt=""></td>
    	</tr>
    	<tr>
    		<td colspan="2" rowspan="2">
    			<img src="images/index_16.png" width="123" height="49" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="23" alt=""></td>
    	</tr>
    	<tr>
    		<td colspan="2">
    			<img src="images/index_17.png" width="101" height="26" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="1" height="26" alt=""></td>
    	</tr>
    	<tr>
    		<td>
    			<img src="images/spacer.gif" width="86" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="92" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="15" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="14" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="7" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="116" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="3" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="99" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="64" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="37" height="1" alt=""></td>
    		<td>
    			<img src="images/spacer.gif" width="47" height="1" alt=""></td>
    		<td></td>
    	</tr>
    </table>
    

    If you take a look at the page, you'll notice it does exactly what it's supposed to do. It's when you look "under the hood" you'll see the mess, not to mention, adding additional functionality to the image map, like hover states, usually requires javascript.

    Semantics and the taboo of table based web design aside, how much of a nightmare is it to edit that? I mean, if you decided to change the source image whatsoever there's a lot of legwork to be done to re-export it into HTML, especially if your 17 images becomes 25 images. My stomach turns just to think about it.

    What we're going to do is cut the HTML into a fraction of what you see above, while using only one image and CSS. Before we do, let's take a look at what we're going to be covering in this tutorial.

    1. Recreate our image map using CSS
    2. Add hover states using a CSS background sprite
    3. Create a "tooltip" like pop-up when hovering "hotspots"

    First, we'll be recreating the above mess in a much leaner, easily manageable, and semantic way. Once we get our feet wet and get comfortable with how CSS image maps work, we'll kick it up a notch and use a CSS background image sprite to add some hover states to our image map. Lastly, as if that wasn't enough, we'll take it even one step further and demonstrate how an image map's use is not necessarily restricted to its navigational purposes, but proves to be quite useful in providing additional information about specific sections of the image map using a "tooltip" like pop-up.

    Noobnote:

    As pointed out in the comments, I don't want to confuse a beginner with the HTML map tag and image slicing. I refer to the above example as "image map" when, in all actuality, it is a way of slicing and image into a table and creating its hotspots that way. I have always just referred to both ways as image mapping, and I apologize for any confusion.

    Recreate our image map using CSS

    In the table based image map, I used Photoshop to slice my continents map into 17 different images to be used for my 6 hotspots, or clickable links. In our CSS image map, we're only going to use one image.

    map-color.png

    tutorial image
    source

    Apparently Wikipedia didn't want to include Antarctica in the continents map so we'll be working with 6 instead of 7! Oh well, the important lesson here is CSS image maps, not geography. ;)

    What we're going to do is create an unordered list and use our map-color.png as its background-image. Fire up your text-editor of choice and create a new html document with the following markup in it:

    <ul id="continents">
        <li id="northamerica"><a href="http://en.wikipedia.org/wiki/North_America">North America</a></li>
        <li id="southamerica"><a href="http://en.wikipedia.org/wiki/South_America">South America</a></li>    
        <li id="asia"><a href="http://en.wikipedia.org/wiki/Asia">Asia</a></li>
        <li id="australia"><a href="http://en.wikipedia.org/wiki/Australia">Australia</a></li>
        <li id="africa"><a href="http://en.wikipedia.org/wiki/Africa">Africa</a></li>
        <li id="europe"><a href="http://en.wikipedia.org/wiki/Europe">Europe</a></li>    
    </ul>
    

    What we have here is a simple unordered list containing our 6 continents and links to their information on wikipedia.org.

    Let's add some basic styles to our ul element.

    ul#continents {
    	list-style: none;
    	background: url(images/map-color.png) no-repeat 0 0;
    	position: relative;
    	width: 580px;
    	height: 268px;	
    	margin: 0;
    	padding: 0;
    }
    

    Some basic styles here, nothing special. We removed the list-style, added a background image, declared its width and height to be the same as our background image, and reset the margins and padding. The important declaration to pay attention to here is its position value of relative. This will allow for us to absolutely position our li elements next. You should have something like this.

    Now we need to position our li elements according to their size and location on the continents map.

    tutorial image

    To make it easier for positioning our li elements, I recommend adding a 1px border to each li element, to guide us as a wireframe. We can remove the border when we're done positioning our li elements. Also, because all of our li elements will have this border AND also have a position value of absolute, rather than repeating ourself on each element's declarations, let's add them to all the li elements in our unordered list. Below your ul#continents styles add the following styles:

    ul#continents li {
    	border: 1px solid #000;	
    	position: absolute;
    }
    

    If you look at the page now, you'll see all of our li elements all bunched up in the left hand corner, overlapping one another. This is because they all have an absolute position, but there aren't any left, top, right, or bottom property values declared yet. We'll do that next.

    Right below the ul#continents li styles, add the following styles:

    #northamerica {
    	width: 227px;
    	height: 142px;
    	top: 2px;
    	left: 0px;
    }
    
    #southamerica {
    	width: 108px;
    	height: 130px;
    	top: 131px;
    	left: 76px;
    }
    
    #africa {
    	width: 120px;
    	height: 140px;
    	top: 83px;
    	left: 209px;
    }
    
    #europe {
    	width: 120px;
    	height: 84px;
    	top: 1px;
    	left: 211px;
    }
    
    #asia {
    	width: 215px;
    	height: 175px;
    	top: 1px;
    	left: 283px;
    }
    
    #australia {
    	width: 114px;
    	height: 95px;
    	top: 152px;
    	left: 432px;
    }
    

    All we did was give each li element its respected height and width values, and also positioned each of them using the left and top properties.

    Noobnote:

    An easy way to get an idea of where to start with our left and top properties, I simply used Photoshops rectangle marquee selection tool and the "info" panel to give me an estimate. Once I had an approximate value from there, I fine tuned it in the CSS declarations.
    tutorial image

    Now if you take a look at our page, you'll see a nice little wireframe of our image map. This is where the borders come in handy for fine tuning the positions. I think it looks good where it is, let's move on!

    Right now, our li elements are only "clickable" within the text area of each anchor element. We want our entire li element to be clickable, so we need to add some styles to our anchor elements. I've highlighted the new styles needed.

    ul#continents {
    	list-style: none;
    	background: url(images/map-color.png) no-repeat 0 0;
    	position: relative;
    	width: 580px;
    	height: 268px;	
    	margin: 0;
    	padding: 0;
    }
    
    ul#continents li {
    	border: 1px solid #000;	
    	position: absolute;
    }
    
    ul#continents li a{
    	display: block;
    	height: 100%;
    	text-indent: -9000px;
    }
    
    #northamerica {
    	width: 227px;
    	height: 142px;
    	top: 2px;
    	left: 0px;
    }
    
    #southamerica {
    	width: 108px;
    	height: 130px;
    	top: 131px;
    	left: 76px;
    }
    
    #africa {
    	width: 120px;
    	height: 140px;
    	top: 83px;
    	left: 209px;
    }
    
    #europe {
    	width: 120px;
    	height: 84px;
    	top: 1px;
    	left: 211px;
    }
    
    #asia {
    	width: 215px;
    	height: 175px;
    	top: 1px;
    	left: 283px;
    }
    
    #australia {
    	width: 114px;
    	height: 95px;
    	top: 152px;
    	left: 432px;
    }
    

    First thing we did was declare our display property value as block, instead of its default of inline, allowing for us to give it a height value, in which case we're giving it a value of 100%. We also gave it a negative text-indent value, because we want our text to only exist if CSS is disabled, otherwise we don't want it shown on our image map. Take a look at our page now, and hover over our "hotspots". You'll notice now that our hotspots include our entire li element now.

    That's it! You can now remove the border values from your li elements and you'll have a simple CSS image map.

    Not only is the markup cleaner, and easier to read, but, it's also a lot easier to make modifications to. For instance, if we went in and added Antarctica to the map, we'd simply need to create another li element and position it accordingly.

    Add hover states to our map

    So far we have demonstrated how to implement a simple image map using CSS, but, from a presentational standpoint, there doesn't seem to be much of a difference between the two image maps (table based vs. CSS). Where CSS image maps stand out is the ability to use a CSS background image sprite to add hover states to our image map.

    I used another map from wikipedia to create a simple image sprite.

    map.png
    tutorial image

    As you can see, I created a new image called map.png. I'm going to replace the map-color.png in my ul#continents styles with my new sprite image.

    ul#continents {
    	list-style: none;
    	background: url(images/map.png) no-repeat 0 0;
    	position: relative;
    	width: 580px;
    	height: 268px;	
    	margin: 0;
    	padding: 0;
    }
    

    If you were to look at your page with the new background image, you wouldn't see a difference. What we need to do is add background image to our anchor elements, but only when they are hovered. What we're going to do is use the same background image as our ul#continents element on our anchor elements when they are hovered. We'll then need to position the background image according to its position within the sprite.

    tutorial image

    Let's go ahead and start with North America. At the bottom of your styles, add the following CSS rules:

    ul#continents li a:hover {
    	background: url(images/map.png) no-repeat 0 0;	
    }
    
    ul#continents li#northamerica a:hover {
    	background-position: 0 -270px;
    }
    

    What we've done is added our background image (map.png) to all of our anchor elements within the continents unordered list. We then declared the background position on our anchor element inside of the li#northamerica. If you take a look at our image map now, you'll see when you hover North America, it has a nice little hover state letting you know you're hovering over North America. If you hover the other continents, you'll see a piece of the the colored version of North America, because we haven't changed their background-positions yet. We'll do that now.

    Below the last CSS rules you just put in your styles, go ahead and add the rest of the background position values.

    ul#continents li#southamerica a:hover {
    	background-position: -226px -273px;
    }
    
    ul#continents li#africa a:hover {
    	background-position: -209px -417px;
    }
    
    ul#continents li#europe a:hover {
    	background-position: -22px -427px;
    }
    
    ul#continents li#asia a:hover {
    	background-position: -363px -268px;
    }
    
    ul#continents li#australia a:hover {
    	background-position: -412px -455px;
    }
    

    Take a look at our image map now, and you'll see the background positions itself accordingly, when you hover over each continent.

    How easy was that?! That's a nice little image map, with minimal markup, done 100% with CSS, and no javascript.

    Here's our image map CSS and HTML up to this point:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>CSS Image Maps - With Hover States</title>
    <style type="text/css">
    ul#continents {
    	list-style: none;
    	background: url(images/map.png) no-repeat 0 0;
    	position: relative;
    	width: 580px;
    	height: 268px;	
    	margin: 0;
    	padding: 0;
    }
    
    ul#continents li {
    	position: absolute;
    }
    
    ul#continents li a{
    	display: block;
    	height: 100%;
    	text-indent: -9000px;
    }
    
    #northamerica {
    	width: 227px;
    	height: 142px;
    	top: 2px;
    	left: 0px;
    }
    
    #southamerica {
    	width: 108px;
    	height: 130px;
    	top: 131px;
    	left: 76px;
    }
    
    #africa {
    	width: 120px;
    	height: 140px;
    	top: 83px;
    	left: 209px;
    }
    
    #europe {
    	width: 120px;
    	height: 84px;
    	top: 1px;
    	left: 211px;
    }
    
    #asia {
    	width: 215px;
    	height: 175px;
    	top: 1px;
    	left: 283px;
    }
    
    #australia {
    	width: 114px;
    	height: 95px;
    	top: 152px;
    	left: 432px;
    }
    
    ul#continents li a:hover {
    	background: url(images/map.png) no-repeat 0 0;	
    }
    
    ul#continents li#northamerica a:hover {
    	background-position: 0 -270px;
    }
    
    ul#continents li#southamerica a:hover {
    	background-position: -226px -273px;
    }
    
    ul#continents li#africa a:hover {
    	background-position: -209px -417px;
    }
    
    ul#continents li#europe a:hover {
    	background-position: -22px -427px;
    }
    
    ul#continents li#asia a:hover {
    	background-position: -363px -268px;
    }
    
    ul#continents li#australia a:hover {
    	background-position: -412px -455px;
    }
    
    </style>
    </head>
    
    <body>
    <ul id="continents">
        <li id="northamerica"><a href="http://en.wikipedia.org/wiki/North_America">North America</a></li>
        <li id="southamerica"><a href="http://en.wikipedia.org/wiki/South_America">South America</a></li>    
        <li id="asia"><a href="http://en.wikipedia.org/wiki/Asia">Asia</a></li>
        <li id="australia"><a href="http://en.wikipedia.org/wiki/Australia">Australia</a></li>
        <li id="africa"><a href="http://en.wikipedia.org/wiki/Africa">Africa</a></li>
        <li id="europe"><a href="http://en.wikipedia.org/wiki/Europe">Europe</a></li>    
    </ul>
    </body>
    </html>
    

    Create a "tooltip" like pop-up when hovering our continents

    By now, you should probably have a pretty good feeling about CSS image maps and how to implement them. What we're going to do now is take it one step further and create a "tooltip" like pop-up to each of our continents when they are hovered. First thing we need to do is remove the negative text-indent declaration from our anchor elements.

    ul#continents {
    	list-style: none;
    	background: url(images/map.png) no-repeat 0 0;
    	position: relative;
    	width: 580px;
    	height: 268px;	
    	margin: 0;
    	padding: 0;
    }
    
    ul#continents li {
    	position: absolute;
    }
    
    ul#continents li a{
    	display: block;
    	height: 100%;
    
    }
    

    Now when you view your image map, you'll notice that all the country named anchors are visible again. We're going to take care of that now.

    In the HTML of our image map, wrap the text content of each of our anchor elements in a <span> tag. So our html in the body should now look like this:

    &lt;ul id=&quot;continents&quot;&gt;
        &lt;li id=&quot;northamerica&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/North_America&quot;&gt;&lt;span&gt;North America&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;southamerica&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/South_America&quot;&gt;&lt;span&gt;South America&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;    
        &lt;li id=&quot;asia&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Asia&quot;&gt;&lt;span&gt;Asia&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;australia&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Australia&quot;&gt;&lt;span&gt;Australia&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;africa&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Africa&quot;&gt;&lt;span&gt;Africa&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;europe&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Europe&quot;&gt;&lt;span&gt;Europe&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;    
    &lt;/ul&gt;
    

    If you look at our image map now, you won't see any changes in our layout. Now, as I said, we only want text to show when we hover over one of our continents. So what we need to do is hide our span elements until our anchor element has been hovered. We will do this by using the display property.

    Add the following CSS rules at the bottom of your styles:

    ul#continents li a span {
    	display: none;
    }
    
    ul#continents li a:hover span {
    	display: block;
    }
    

    What we're telling the browser to do is hide all span elements (display: none) until its parent anchor element is hovered, in which, it will change from display: none to display: block.

    Take a look at our image map now and hover each continent. You'll now see the text will only show when its parent anchor element has been hovered. Let's make this thing look better, and add some useful information to the span elements, rather than just the country's name.

    Note: I changed the way my HTML markup looks in the unordered list code for readability, it will not render the page any differently.

    &lt;ul id=&quot;continents&quot;&gt;
        &lt;li id=&quot;northamerica&quot;&gt;
            &lt;a href=&quot;http://en.wikipedia.org/wiki/North_America&quot;&gt;
                &lt;span&gt;
                    &lt;strong&gt;North America&lt;/strong&gt; 
                    Population: 528,720,588
                &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;
        &lt;li id=&quot;southamerica&quot;&gt;
       		&lt;a href=&quot;http://en.wikipedia.org/wiki/South_America&quot;&gt;
                &lt;span&gt;
                    &lt;strong&gt;South America&lt;/strong&gt;
                    Population: 385,742,554
                &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;    
        &lt;li id=&quot;asia&quot;&gt;
        	&lt;a href=&quot;http://en.wikipedia.org/wiki/Asia&quot;&gt;
                &lt;span&gt;
                    &lt;strong&gt;Asia&lt;/strong&gt;
                    Population: 3,879,000,000
                &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;
        &lt;li id=&quot;australia&quot;&gt;
        	&lt;a href=&quot;http://en.wikipedia.org/wiki/Australia&quot;&gt;
                &lt;span&gt;
                    &lt;strong&gt;Australia&lt;/strong&gt;
                    Population: 21,807,000
                &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;
        &lt;li id=&quot;africa&quot;&gt;
            &lt;a href=&quot;http://en.wikipedia.org/wiki/Africa&quot;&gt;
            &lt;span&gt;
                &lt;strong&gt;Africa&lt;/strong&gt;
                Population: 922,011,000
            &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;
        &lt;li id=&quot;europe&quot;&gt;
            &lt;a href=&quot;http://en.wikipedia.org/wiki/Europe&quot;&gt;
                &lt;span&gt;
                    &lt;strong&gt;Europe&lt;/strong&gt;
                    Population: 731,000,000
                &lt;/span&gt;
            &lt;/a&gt;
        &lt;/li&gt;    
    &lt;/ul&gt;
    

    What we've done is wrap <strong> tags around our each of our continents' names and added each of their respected population counts.

    Now we need to add some styles to our new elements and we're done. First thing we want to do is remove the underline from our anchor elements. Go ahead and find your ul#continents li a rules in your styles and add the highlighted style declaration.

    ul#continents li a{
    	display: block;
    	height: 100%;
    	text-decoration: none;
    }
    

    Now we need to add some more rules to our span elements. Add the highlighted styles.

    ul#continents li a:hover span {
    	display: block;
    	padding: 5px;
    	width: 150px;
    	background: #000;
    	position: relative;
    	top: 50%;
    	font: 11px Arial, Helvetica, sans-serif;
    	opacity: .75;
    	filter:alpha(opacity=75);
    	color: #FFF;
    }
    

    What we're doing here is giving our span elements (when they're parent anchor element is hovered) some styles to make our content look a little better and more like a tooltip.

    Noobnote:

    If you're not familiar with the opacity or filter:alpha properties, they're simply being used to drop the opacity of our span elements. Please note that these two declarations will cause your CSS to not validate. You could use javascript (jQuery) to apply the opacity settings if you wanted to, but for the sake of this demonstration I'm not going to worry about my CSS validating. Just know, without these two declarations your CSS and HTML to this point will certainly validate.

    Take a look at our image map now, and you'll see we have implemented a nice little tooltip, when hovering each continent, giving us each of their populations!

    Final Thoughts

    That's it! Now, you should have a pretty good understanding of how CSS image maps work. You should be able to take this knowledge and use it into your own projects, or even extend its use by implementing your own ideas.

    As always, if you have any questions or problems, please don't be afraid to ask in the comments.

    Thanks for reading.