Earlier today I came across a link to the website responsiveicons.co.uk. It's an approach that builds upon a basic icon with a more complex version as the viewport increases.

From the site....

'Responsive Icons' is a project that explores scalable iconography. The concept interrogates the varying complexity of symbols used on devices, and aims to find the perfect balance of simplicity in relation to screen size. The project is currently in its early stages and I will be adding additional icons to the site shortly. Best viewed on desktop (scale your browser width to see the adaptions).

Quote by creator Joe Harrison

I thought this was a fantastic idea and a great approach so I poked around how it was being done.

Initial approach

While I think it's a wonderful idea the actual approach taken leaves a little bit to be desired.  With that in mind I'm not being nasty, at the moment it's a concept that needs to go through the usual rounds of feedback before we get to something production ready.

The current approach has a series of <img> tags wrapped with <div> tags.  As the viewport changes the appropriate media query will fire applying CSS that will show the relevant SVG icon while hiding the remaining.

The Code

This is the HTML

<div id="main_icon" style="margin-left: -300px;">
<div id="icon_0"><img src="_client/images/icons/home/home_icon_0.svg"></div>
<div id="icon_1"><img src="_client/images/icons/home/home_icon_1.svg"></div>
<div id="icon_2"><img src="_client/images/icons/home/home_icon_2.svg"></div>
<div id="icon_3"><img src="_client/images/icons/home/home_icon_3.svg"></div>
<div id="icon_4"><img src="_client/images/icons/home/home_icon_4.svg"></div>
<div id="icon_5"><img src="_client/images/icons/home/home_icon_5.svg"></div>
<div id="icon_6"><img src="_client/images/icons/home/home_icon_6.svg"></div>
<div id="icon_7"><img src="_client/images/icons/home/home_icon_7.svg"></div>
<div id="icon_8"><img src="_client/images/icons/home/home_icon_8.svg"></div>
</div>

And here is some of the CSS

@media (max-width: 1000px) {
#icon_6 {display:block;}
#icon_7 {display:none;}
}
@media (max-width: 900px) {
#icon_5 {display:block;}
#icon_6 {display:none;}
}

The Problem with this approach

This approach suffers the same issue that plagues us with a lot of our responsive image issues. The browser will still download an <img> tag even if it is set to display:none; in the CSS. This is one of the added "benefits" that comes with the prefetching browsers of today.

Although each of the SVG images are only a few bytes in size there is still an additional http request for every img tag regardless if they will ever be seen.

An alternative solution

This is by no means an original idea. 

I had a hunch that the original implementation would suffer the multiple download issue so my mind jumped straight to the clown car image technique that Estelle Way came up with on Smashing Magazine.

The idea Estelle had with the clown car approach is to contain an SVG image that contained media queries that would apply a background image to the SVG, and because the background images were specified as part of the CSS they would not be downloaded until they were displayed.

While this got me started along the solution, I hit a few snags trying to keep my SVG code clean enough to work. It wasn't until I came across Chris Coyiers article about Using SVG that I realised I could apply classes to the SVG points themselves and everything fell in to place.

Step 1: Combine the SVG's 

I downloaded all of the SVG files required. I copied the <polygon> and <path> items from each of the SVG files and added them all to a single SVG file.

Here is a subset showing two of the icons together...

<polygon fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>
<polygon fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>

Step 2: Add classes to each sub SVG

I added classes to each of the icon sections I had copied over. The smaller icons only had a single set of points, however as they became more complex I either added classes to the surrounding element or to each of the elements (see further down in the final result)

<polygon class="smallest" fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>
<polygon class="small" fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>

Step 3: Set Media Queries

This throws back to the clown car technique. SVG files can contain <style> tags and will evaluate @media queries as well.

<style>
svg {
background-size: 100% 100%;
background-repeat: no-repeat;
}
@media (min-width: 0px) {
.small, .medium, .large, .largest { display:none; }
.smallest {display:block;}
}
@media (min-width: 400px) {
.smallest {display: none;}
.medium {display:block;}
}
@media (min-width: 600px) {
.medium {display: none;}
.large {display:block;}
}
@media (min-width: 800px) {
.large {display: none;}
.largest {display:block;}
}
}
</style>

Now I had...

  • a single SVG file (1 http request) that was only a few kilobytes in size
  • Classes applied to each sub icon so I could target them individually
  • Media queries to determine which style would be viewed at whatever breakpoint

With all these things in place I could simply add a single <img src="responsiveicon.svg" /> element and have all the benefits.  Hooray!  Check out the code pen below plus the full SVG file code.

Final Solution

SVG Image Code

<svg
viewBox="0 0 300 329" preserveAspectRatio="xMidYMid meet">
<title>Responsive Icons</title>
<style>
svg {
background-size: 100% 100%;
background-repeat: no-repeat;
}
@media (min-width: 0px) {
.small, .medium, .large, .largest { display:none; }
.smallest {display:block;}
}
@media (min-width: 400px) {
.smallest {display: none;}
.medium {display:block;}
}
@media (min-width: 600px) {
.medium {display: none;}
.large {display:block;}
}
@media (min-width: 800px) {
.large {display: none;}
.largest {display:block;}
}
}
</style>
<polygon class="smallest" fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>
<polygon class="small" fill="#E55C3C" points="162.5,166.5 162.5,148.5 150.5,136.5 138.5,148.5 138.5,166.5 "/>
<path class="medium" fill="#E55C3C" d="M84.5,239.5h23v-52h30v52h79v-89h-132V239.5z M161.5,203.5v-16h15v16H161.5z M181.5,203.5v-16h15v16H181.5z
M196.5,166.5v16h-15v-16H196.5z M176.5,166.5v16h-15v-16H176.5z"/>
<polygon class="medium" fill="#E55C3C" points="64.5,144.5 236.5,144.5 150.501,58.907 112.5,97.5 112.5,82.5 117.5,82.5 117.5,74.5 84.5,74.5
84.5,82.5 90.5,82.5 90.5,118.5 "/>
<g class="large">
<path fill="#E55C3C" d="M69.5,259.5h21v-5h5v-55h39v55h5v5h94v-106h-164V259.5z M189.5,173.5h19v20h-19V173.5z M189.5,199.5h19v20
h-19V199.5z M164.5,199.5h19v20h-19V199.5z M164.5,173.5h19v20h-19V173.5z M159.5,224.5h54v5h-54V224.5z"/>
<polygon fill="#E55C3C" points="40.5,148.5 260.5,148.5 150.782,39.5 103.5,86.5 103.5,74.5 74.5,74.5 74.5,115.5 "/>
<rect x="68.661" y="59.5" fill="#E55C3C" width="38.839" height="10"/>
<path fill="#E55C3C" d="M102.5,232c-1.176,0-2.5-1.324-2.5-2.5c0-1.177,1.324-2.5,2.5-2.5c1.177,0,2.5,1.323,2.5,2.5
C105,230.676,103.677,232,102.5,232z"/>
</g>
<rect x="223.059" y="114.927" fill="#E55C3C" width="63.441" height="146.573" class="largest"/>
<polyline class="largest" fill="#B8492E" points="164.5,59.5 254.369,59.5 298.798,122.5 164.5,122.5 164.5,59.5 "/>
<polygon class="largest" fill="#E55C3C" points="74.469,70.021 40.678,103.812 40.678,57.5 35.5,57.5 35.5,46.5 79.5,46.5 79.5,56.447
74.469,56.447 "/>
<polygon class="largest" fill="#B8492E" points="74.279,59.5 74.279,71.206 40.488,104.997 40.488,93.333 "/>
<path class="largest" fill="#E55C3C" d="M95.475,204.613V275.5h57.017v-70.887H95.475z M115.427,246.568c-1.462,0-3.108-1.646-3.108-3.107
c0-1.463,1.646-3.107,3.108-3.107c1.463,0,3.107,1.646,3.107,3.107C118.535,244.922,116.89,246.568,115.427,246.568z"/>
<path class="largest" fill="#E98158" d="M-0.5,144.5h17v137h78v-6h6v-70h48v70h6v6h80V143.652l0.798,0.848h17.503L125.158,17.586L-0.5,144.5z
M57.5,204.5v-17h16v17H57.5z M73.5,209.5v16h-16v-16H73.5z M35.5,204.5v-17h16v17H35.5z M51.5,209.5v16h-16v-16H51.5z M77.5,234.5
h-46v-5h46V234.5z M122.5,134.5h-15v-20h15V134.5z M122.5,109.5h-15v-6.617c0-8.207,8-15.021,15-16.463V109.5z M142.5,134.5h-15v-20
h15V134.5z M142.5,109.5h-15V86.258c9,0.996,15,8.053,15,16.625V109.5z M219.5,234.5h-46v-5h46V234.5z M193.5,209.5v16h-16v-16
H193.5z M177.5,204.5v-17h16v17H177.5z M215.5,209.5v16h-16v-16H215.5z M199.5,204.5v-17h16v17H199.5z"/>
<polygon class="largest" fill="#B8492E" points="141.5,275.5 141.5,211.5 100.5,211.5 100.5,205.5 148.5,205.5 148.5,275.5 "/>
</svg>