CSS Hoverable Icon with SVG




So it all started with the rather trivial task of adding icons with hover states to a list of downloads. After spending way to much time on such a simple task, the result was learning quite a bit about different ways to use SVG in combination with CSS so I thought I would share the process and what I learned. If you want to skip over my ramblings and go right to the end go to the final solution.

  1. The Problem
  2. The Restrictions
  3. Restricted Solutions

    1. Simple Rollover
    2. Image Sprite
    3. Inject SVG in Document and utilize <use>
  4. Future Solutions that Aren’t Yet Compliant
    1. Image Mask
  5. Other Solutions
    1. Server-Side image manipulation
    2. Javascript manipulation
    3. Icon Fonts
  6. My Solution
    1. Make an SVG Icon
    2. Use a::begin
    3. style background image? nope
    4. use content: url(‘data:)
  7. The Final Solution
  8. The Final Solution++

The Problem

Basically I needed to change this mark up:

From looking like this:

To include an icon with comparable color change on hover looking like this:

Restrictions

Easy you say? Why yes it is. I probably made my first rollover menu icon back in the mid 90s. However, I would rather not mix in my presentation/css code with my content/html code. Also if I need to change icon colors I would rather do it in one spot and not have to regenerate any images. Thus the following two restrictions:

  1. Don’t add/change anything in the html markup
  2. Control both link text color and icon color via css

Restricted Solutions

So the following are brief explanations of ways to do that which work perfectly fine but do not satisfy the above restrictions.

Simple Rollover

So image rollovers have been around for ever and would be a perfectly fine way to handle this problem. The basic strategy is to do the following:

  • Create two separate images representing the hover and regular states of the icon.
  • Use css to change which image is loaded based upon the hover state.

In our case we could leverage the list-style-image attribute and the css would look something like this

This works perfectly fine and it satisfies restriction #1. However we have no control over the color of the icon via css, so if our page style changes, we’ll have to generate icon image files to match so it doesn’t satisfy restriction #2.

Image Sprite

The image sprite solution is very similar to the rollover image solution. The big difference is that you only have one actual file with multiple versions of the same image repeated for each state. Then for the hover state you just shift the background-position in the css to the location in the image of the color you want

So, again, this works well and satisfies restriction #1 for not changing the html. You could say it “kind of” satisfies restriction #2 if you put enough possible image colors in it that you wouldn’t have to generate any more images. But this is really kind of cheating as it doens’t give you total control over the color in css.

For a quick overview of image sprites see W3 Schools Image Sprites

Inject SVG in Document and utilize <use>

So this is probably my favorite solution to the problem that doesn’t pass the restrictions. The basic idea is instead of using an image defined in a separate file, you embed the definition for the svg icon on the page and the you use it, and more importantly, style it with css. Here is an example of how it would work.

HTML:

CSS:

The reason that I really like this option is that although it is necessary to alter the markup, maintenance from that point forward is a snap. If you want to change the colors or size of the icons, all you have to do is update the css, which is really how it should be.
Here is an article explaining a solution for wiring a set up icons up:
https://css-tricks.com/svg-sprites-use-better-icon-fonts/.

Once compliance catches up it may even be possible to use an external svg image instead of embedding it in the page.

For a deeper discussion with even more info in the comments see:
https://css-tricks.com/svg-use-with-external-reference-take-2/

Future Solutions that Aren’t Yet Compliant

Image Mask/Clipping

This solution gets around the inability to change the color of images by instead changing the background-color and using an image mask or image clipping. An image mask is used to restrict parts of an element (usually an image) from showing. So if you construct your mask to be the exact opposite of your image, then when applied the background will show through looking exactly like your image.

There are several different techniques that can be used but at present browser compatibility is sketchy for general html elements. A good article listing the various techniques can be found at https://css-tricks.com/clipping-masking-css/.

Here are some demos of various masking techniques and compatibility:
http://codepen.io/yoksel/full/fsdbu/

A Successful Masking Strategy

There is one way to achieve image masking that is fully compatible with all browsers: create an image which is transparent in the shape of the icon and otherwise matches the color of the page background. This not only works but gets around almost all of the restrictions. The one slight hitch is that you cannot change the page background without updating the icon image to match. But the good news is that this can be done entirely in the css file and no markup changes are needed.

Other Solutions

So if you are willing to stray from strict html and css there are quite a options, especially if you don’t mind diving into javascript or backend programming

Server-Side image manipulation

The crux of the issue is that we need to make the colors of the icon customizable. An easy way to do that is to allow the user to set the color values needed in their http request for the image and have the server take care of altering the color. SVG images make this a snap. Here is an example of how this would work using PHP as your backend technology:”

CSS:

PHP:

Javascript manipulation

So if you allow javascript there are many ways to accomplish hoverable icons. Usually these strategies include loading some svg snippets, perhaps via ajax, and then inserting them into the DOM where you want the icons to appear. An example of this is iconic.js. One common downside is it can lead to DOM flicker. However, once the svg snippets have been inserted into the DOM via javascript it can be much easier to style the internals of the svg via css.

Icon Fonts

The Icon Font solution gets around the image color problem by treating all your icons as characters instead of images. So once you load the font up you can then style the icon however you want. This allows really nice flexibility for single-color icons. The Font Awesome is an example of how this can work. Using it straight out of the box requires some html manipulation and therefore does not satisfy our restrictions, but it gets the job done.

HTML

This works by inserting the download character into the ::before sudo element, but it necessitates adding the <i> tag. You can get around the necessity to add the tag by manually specifying the css for your element. This method satisfies all of the restrictions but does lock you into whatever icons are provided by the icon font you are using. Here is an example of how the manual usage of the font icons in Font Awsome could work:

My Solution

So the solutions we’ve gone over so far are the following:

  • Standard Rollover
  • Image Sprite
  • SVG injection
  • Image Mask
  • Server Side Images
  • Javascript Manipulation
  • Icon Fonts

Each of these solutions have their benefits. But none of the above are a purely CSS solution to the problem of hoverable icons with color customizable in CSS. What follows is the path I took in search of this solution.

Make an SVG Icon

First of all, if we are talking about changing the color of images, I figured that SVG is the way to go. SVG is scalable, and styles such as color can be controlled via css, so I figured my first task was to find a simple svg download icon. And since icons are usually pretty simple images, I figured I would take the time to actually make one myself. In the end it turned out to be rather easy and painless.

SVG is an acronym for “Scalable Vector Image” which means instead of defining points in image you define shapes and paths. A nice resource for this is The Pocket Guide to Writing SVG. Our icon is quite simple and consists of two parts: the down arrow and the tray. This translates into two paths in SVG. All I had to do was figure out the coordinates of the corners in a 48×48 square and there is my icon. In this case there are 7 corners in the arrow and 8 in the tray.

This translates to the following SVG

Now all we have to do is save this to a file and we should be able to use it just fine.

Use a::begin

Now that I had the file going, I needed to use css to attach it to each <a> tag in my download links list. There are several ways to do this including setting it as the list-style-image of the li element. That would work, but there is really no way to add style information to the image referenced this way, so instead I opted for using the a::begin pseudo element and set our newly created image as the content.

All that remains is to use css to change the color!

Style referenced image? Nope!

You would think at this point all that would be necessary is to add some css to change the fill attribute of our icon when hovering. Something like the following:

Unfortunately, this doesn’t work! This is because (at least for now) styles don’t apply across document boundaries. Your page CSS cannot effect the internals of the svg file you are referencing (see this stack overflow entry for more info).

use content: url(‘data:)

Fortunately there is a way to use CSS to style SVG data without having to embed the SVG data in your html (see Inject SVG in Document and utilize <use> above). The solution is to actually embed the SVG image in the CSS. Why is this awesome? Because an icon is really a style so now you have all your style info in one place. You don’t have to worry about making sure your css path and your image paths all line up. Instead of using CSS to reference a url to an SVG image file, you can insert the actual contents of the SVG file direclty into your CSS. In our case this is how it looks:

This works on some browsers (chrome) but to get it be compliant across browsers you will need to url encode any characters that need it (e.g turn “<” characters into “%3c”) and also create the hover color. This brings us to our…

The Final Solution

Final Solution

Pros

  • No need to change html markup!
  • Color configurable from CSS.
  • No need to link separate image files into your css.

Cons

  • Converting SVG data into readable CSS with correct encodings
  • Repeating image definition in CSS for just for a change of color.

Final Solution++: use LESS/SASS

So this solution works nice, but it really isn’t ideal since you need to repeat the definition of the SVG whenever you want to change the color. However, using a CSS preprocessor can help you get around that problem. It also lets you handle the url encoding which removes both of the stated “cons” above. Here is the example in LESS

This solution is nice because you could define a whole set of svgs in one css file and use the color variables to change all of them at the same time. Then you have a whole icon system defined in one single file. In fact, I like that idea so much I might just go ahead and try it.

Leave a Comment