Developers and designers use a variety of techniques to create site icons that change on hover or use, often by drawing different icon states in sprite sheets. These techniques are labor-intensive, and make later design changes difficult. More advanced techniques, such as transitioning changes in SVG icons via CSS, are an improvement, but still must be written on a per-icon basis.

Ideally, we would create icons just once, in their final state, and use CSS to present them in an “inactive” state, switching to “active” with a single declaration. With CSS blend modes and filters, you can do just that.

Inverting A Link

A good example might be a variation on the link to CodePen that appears at the bottom of many articles on this site:

<a href="//codepen.io/dudleystorey/pen/KAJtc" id="codepen">
	<img src="codepen-fill-white.svg" alt>Experiment with this code on CodePen
</a>

This markup has the obvious disadvantage that every time the link is created, the SVG image must be inserted within it, which also makes it difficult to change. The CSS for the element reflects this complication:

a#codepen {
	display: block;
	clear: both;
	text-decoration: none;
	margin: 2rem 0;
	background: rgba(0,0,0,0.2);
	padding: .6rem;
	transition: .6s;
}
a#codepen img {
	width: 60px;
	height: 60px;
	vertical-align: middle;
	margin-right: 2rem;
}

Both markup and presentation could be cleaned up substantially by placing the image as a to the element, rather than an actual <img> tag. The HTML would become: <a href=//codepen.io/dudleystorey/pen/KAJtc id=codepen>Experiment with this code on CodePen</a>

And the CSS:

a#codepen {
  display: block;
  text-decoration: none;
  color: #fff;
  padding: 2rem;
  padding-left: 7rem;
  background: url(codepen-fill-white.svg) #000 no-repeat;
  background-size: 80% 80%;
  background-position: -150% 50%;
}

Let’s say that we wanted to completely reverse the appearance of the link on hover or focus: black elements becoming white and vice-versa. The basics are very easy:

a#codepen:hover, a#codepen:focus {
	background-color: #fff;
	color: #000;
	border: 1px solid #666;
}

But the CodePen logo resolutely remains black on white, meaning that it will look weird in the rollover state for the link. Normally, this would be where icon sprites would be employed, but we can solve the problem adding a single line to the CSS above:

background-blend-mode: difference;

The result: Explore the pen for this project

Filters for Icons

Another example might be the new primary navigation icons I am currently designing for the next version of this site, which you can see at the top of this article. Each icon is a tiny, separate SVG document, loaded as a background image for a series of links:

<nav>
	<a href="/search.php" title="Search"></a>
	<a href="//twitter.com/dudleystorey" title="Twitter"></a>
	<a href="/archive.php" title="Archive"></a>
	<a href="/feed.rss" title="RSS feed"></a>
	<a href="?reverse" title="Change article order to oldest first"></a>
</nav>

The icons are designed and drawn in their “active” state, using attribute selectors:

nav a[href="/search.php"] {
	background: url(search.svg);
}
nav a[href*="twitter"] {
	background: url(twitter.svg);
}
nav a[href="/archive.php"] {
	background: url(calendar.svg);
}
nav a[href="?reverse"] {
	background: url(reverse.svg);
}
nav a[href="/feed.rss"] {
	background: url(rss.svg);
}

For the default navigation, I want every icon to display in the same neutral gray. I can achieve that by reducing their contrast to 0:

nav a {
	width: 50px;
	height: 50px;
	filter: contrast(0);
}

When the user hovers / focuses a link, I change that to full contrast:

nav a:hover {
	filter: contrast(1);
}

Until Microsoft gets its act in gear with regard to filters, IE 10+ users will see the full-color version of the icons in all states (IE9 and earlier will have the proprietary MS-only filter: grey;)

In this second case background-blend-mode: luminosity; and filter: contrast would create pretty much the same results, although there are a few differences to consider between the two:

  1. background-blend-mode: luminosity creates different results depending on the darkness or lightness of the initial color: if an icon is completely black, it has no effect at all, while filter: contrast(0) will wipe every color in the original to a medium grey.
  2. Importantly, that includes white; you have to be careful in distinguishing between something that is transparent in the image or if it is filled with white, as it makes a substantial visual difference when applying filter: contrast.
  3. background-blend-mode requires the addition of a background-color for the element, which may not work for all designs.

As they are under 1K, it will probably be worthwhile to inline the SVG icons with DataURI, another advantage of this technique.