Javascript-Free Image Desaturate

Posted By on Apr 20, 2013 | 2 comments

While designing my portfolio page, I wanted to bring in a simple desaturate effect to my images. After a little searching, I found that this seemed to be a very easy task, so long as I was using some kind of Javascript framework. This seemed to be a bit overkill for my purposes so I set to trying to find a pure (no script) way to accomplish this task. After some research, I found my method: CSS Transitions.

Over the last year or so, the capabilities of web browsers have risen to accommodate the basic use of CSS Transitions in every up-to-date browser. These include size, position, and for the purposes of this tutorial style.

For this tutorial, you need 4 things: some markup, a little CSS, and two images, one a de-saturated version of the other. To see a demonstration, check out my portfolio.




div.image {
	position: relative;
	width: auto;
	height: 100px;
	float: left;
	border: solid 2px #333;
div.image img {
	position: absolute;
	display: block;
	top: 0;
	left: 0;
div.image:hover {
	opacity: 0;
	-webkit-transition: opacity .1s linear .1s;
	-moz-transition: opacity .1s linear .1s;
	-o-transition: opacity .1s linear .1s;
	transition: opacity .1s linear .1s;
	visibility : hidden;
div.image {
	z-index: 21;
	-webkit-transition: opacity .1s linear .1s;
	-moz-transition: opacity .1s linear .1s;
	-o-transition: opacity .1s linear .1s;
	transition: opacity .1s linear .1s;
	visibility : visible;
div.image img.color {
	z-index: 20;

One thing I should explain at this point is that the code in this tutorial does not really transition “between” the two images, but rather creates the illusion of a between transition. We accomplish this by positioning the two given images on top of one another and then adjusting the opacity property of the “top” black-and-white image, revealing the “bottom” colored image. In order to better explain this, let’s break everything down to two separate problems: we need to position the images on top of one another, and we need to be able to transition between the two.

First, you can position two items on top of each other using a little-known CSS trick. Normally, when using the code position: absolute in CSS, it causes the element to jump out of the flow of a website and set its positioning based on the origin point (0,0) of the browser. This usually is the top-left of a browser, causing all kinds of problems for center-oriented or fluid-width websites. Now, with this trick, you can fool the browser and change the origin point for any object.

Starting with the container (Line 1, markup and css), set your CSS to position: relative, then for any children of the container, you set position: absolute. This may seem counter-productive, having differing style declarations, but what this does is actually reset that origin point, making it the top-left of that element, rather than top-left of the browser. From there, a left: 0 and a top: 0 for both items will place both items in the same place, on top of each other. Then, to choose which image would be on top, you set your z-index value. Here, I chose black-and-white to be over the color, by setting the z-index value for color lower than black-and-white.

Now that we have position down, let’s turn to the transition. Because this is not a fully supported feature across all browsers, some care and planning usually has to go into any CSS declaration. First, we want this transition to trigger on hovering (or “rolling”) over the image and there are two images for which to account, so we need to look to the parent element for our trigger. The style selector at line 16 makes use of the :hover pseudo-selector. This tells the browser to use the declarations contained within only if the user have hovered over that element. Since we know this element will contain the two images, we now have our trigger selected out. From there, we add our declarations.

If you want your style to “work” in as many environments as possible, you have to use what I call fallback declarations. Think of Fallback Declarations as synonyms of a normal declaration; they only go into effect if the first declaration is read as invalid. These types of declarations are usually (but not always) a type of declaration called a vendor prefix declaration. As a browser parses CSS declarations in a given style, it moves from top to bottom looking for styles that it recognizes.

For our purposes, let’s look at the style starting at line 16. The transition fallback begins on the second declaration (line 18) with a series of vendor prefix declarations. As the browser parses the declarations, it will either grab its specified declaration, or fall to the next line. After the vendor prefixes, we have the actual CSS3-standard transition declaration. Eventually, the 3 lines of vendor-specific redundancy will not be needed, but for now we must allow for browsers who have yet adopted CSS3. The final line is the “ancient-browser” fix, not even bothering with the opacity property, and opting to either just be visible or not.

From here, we’ve only transitioned to one image, but not back the other way. This is where we set up a similar non-pseudo style block (line 25) that the browser will use for the hover (or “roll”) out event. The only differences between the two style blocks is that one (the hover block) sets opacity of the image to 0% while the other (the non-hover block) returns opacity to 100%. The work’s already done, you just need a little copy/paste and some small changes and you have a JavaScript-free image desaturate effect.


    • Yes, two images. There are some javascript solutions to de-saturate an image, but this method makes it so that computers with no javascript can still get the effect.

      Thanks for the compliments.

      Post a Reply

Submit a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.