Lynskey 2015 Backroad Bike Technical Details DTSwiss TK540/X.9 wheelset Selle Italia X1 Flow Saddle Titanium frame SRAM Apex rear dérailleur
Hover over the bicycle to read descriptions of its components

The venerable imagemap has been with web developers since 1993. One of the first popular ways of creating a website graphical user interface, imagemaps were quickly embraced, pushed way too far, and then neglected as developers moved on to shinier tools such as Flash and JavaScript. However, the <map> tag is still supported in HTML5… and as I’ll demonstrate here, adding CSS reinvigorates the element, making it a real contender against other techniques.

“Product highlight” interfaces are often a variation of the imagemap pattern, but they are rarely responsive or accessible. Extending my experiment with SVG imagemaps, I realised it was possible to make a simple and effective photographic interface with text popups using a minimal amount of markup in .

The Markup

<svg xmlns="" xmlns:xlink="" viewBox="0 0 1000 586" id="lynskey">
    <title>Lynskey 2015 Backroad Bike Technical Details</title>
    <image width="100%" height="100%" xlink:href="lynskey.jpg"></image>
    <circle cx="770" cy="401" r="172"/>
    <text x="400" y="540">DTSwiss TK540/X.9 Wheelset</text>
    <polygon points="256,29 443,29 443,60 357,101 266,65" />
    <text x="80" y="80">Selle Italia X1 Flow Saddle</text>
    <polygon points="646,133 367,159 245,350 283,409 390,434 413,379 456,370 500,400 677,207 684,229 697,217 669,133" />
    <text x="450" y="250">Titanium frame</text>
    <circle cx="225" cy="401" r="51"/>
    <text x="400" y="540">SRAM Apex rear dérailleur</text>

“Hotspots” are constructed from <polygon> and <circle> elements, drawn in Adobe Illustrator. Text descriptions are placed immediately after each hotspot.

SVG is naturally responsive; with the file exported from Illustrator and the code modified so that the image is scaled to 100% of its container, the bitmap is responsive too, meaning that the hotspots will always exactly match the image, whatever its size.


The SVG elements will be filled with black by default, and must be hidden, while remaining active. There’s two ways to achieve this:

  1. Set their opacity to 0;
  2. Set their fill to none and pointer-events to all

For this example, I’ll choose the second option, hiding the text with the first technique:

circle, polygon {
    fill: none;
    pointer-events: all;
text { opacity: 0; }

While it’s entirely possible to link them, for this example I only need the circle and polygon elements to act like links, so I’ll change the cursor when the user hovers over or touches a hotspot:

polygon:hover, circle:hover, 
    polygon:focus, circle:focus { 
		cursor: pointer;

When a hotspot is hovered over, I want the text node immediately after the hotspot to appear:

polygon:hover + text, circle:hover + text,
    polygon:focus + text, circle:focus + text { 
		opacity: 1;

I’ll transition in this change:

svg text { transition: .3s; font-size: 14px; }

Text inside a fluid SVG element is responsive, acting as if it’s sized with vw units. For that reason, you may want to resize the text at certain breakpoints to make it more readable:

@media all and (max-width: 600px) {
	svg text { font-size: 18px; }

Integrating Responsive Images

If the imagemap was intended to be used in fullscreen mode or a wide browser window, it makes sense to load different resolutions of the image to match the viewport, rather than loading a large “one size fits all” bitmap with a corresponding file size. In this case the easiest way to achieve that is to load a smaller background image behind the SVG, with a media query for larger viewport sizes. The <image> is stripped from the <svg> and loaded via CSS instead:

svg#lynskey {
    background-image: url('lynskey-medium.jpg');
@media all and (min-width: 750px) {
    svg#lynskey {
        background-image: url('lynskey-large.jpg');

Of course there’s much more that can be done here, but hopefully this will inspire you to start your own experiments.

Enjoy this piece? I invite you to follow me at to learn more.
Check out the CodePen demo for this article at