How to Build a Grayscale to Color Effect on Scroll (CSS & JavaScript)

In this new tutorial, we’ll start with some grayscale images and learn how to smoothly reveal their colored variants on scroll. To achieve the desired effect, we’re going to take advantage of different modern front-end features like CSS Grid, the clip-path property, and the Intersection Observer API.

Our Grayscale to Color Effect

Without further intro, let’s check out what we’ll be building:

Browser Support

Although this effect has decent browser support, please note that it won’t work in some browsers like the earlier versions of Microsoft Edge. For this example, I’ll concentrate on the technique behind the effect without providing fallbacks for other browsers.

1. Begin With the HTML Markup

We’ll start with four sections:

Inside each section, we’ll put a heading and a fullscreen div wrapper. The wrapper will include two empty divs. Both elements will share the same background image. The first one will show the grayscale version of it, while the last one its original colored version:

The colored image will appear with a slide animation from left to right. Happily enough, we can customize the direction of this animation via the data-animation attribute. All it needs is to add this attribute to the corresponding .colored element with values to-left, to-top, or to-bottom.

2. Define the Styles

With the markup ready, we’ll continue with the main styles.

Utility Classes

For this demonstration, we’ll first define two utility classes which we’ll attach to the target elements: 

Stacking Elements

By default, the divs which hold the images will be stacked on top of another. Only the div with the grayscale image will be visible.

To stack those elements, we’ll take advantage of a CSS Grid technique that I’ve covered in previous tutorials.

Here are the required styles:

Grayscale Images

To create the grayscale images, we’ll use the grayscale() CSS function and pass the value of 1 or 100% as its argument. Also, we’ll give all grayscale images a background color as a fallback until each of those images loads:

Colored Images

As mentioned above, the colored images will initially be hidden. They will become visible with a slide animation as soon as a part of them becomes visible on the page.

Check out a GIF version of the final effect here.

To visually hide them, we won’t use any of the traditional CSS ways like display: none, opacity: 0, and transform: translateX(-100%). In fact, we’re going to try clip-path, a modern CSS property that can help us build interesting effects.

A Quick clip-path Property Explanation

The clip-path property gives us the ability to cut away parts of an element and show only a specific portion of it. The visible area can be represented with different shapes (circles, ellipses, polygons, rectangles).

In our example, we’ll use the inset() function to build the desired rectangle.

In its simplest form, it can receive up to four values in a clockwise direction which specify the side offsets (top, right, bottom, left) that generate the selected area. For simplicity, we can use the margin shorthand which gives us the ability to set all four insets with one, two, three, or four values. Optionally, we can pass some extra values to this function for specifying the rectangle’s roundness.

So, just for practice, let’s assume that we have the following Pixabay image which is 200px by 300px.

Initial image

If we give it clip-path: inset(10px 20px 30px 40px), the resulting image will be 140px by 260px:

Image after clip-path

Going a step further, an element with clip-path: inset(0) means that the whole element will appear. 

On the other hand, an element with one of the four values set to 100% means that it will be squeezed and hidden. Keep in mind that the order of this value inside the function matters and can produce different animations.

Back to our example, here’s how we initially hide the colored images:

3. Animate on Scroll

The colored images will be animated and toggled on scroll. 

To accomplish this task, we’ll take advantage of the Intersection Observer API. 

When at least 50% of each target element enters the viewport, it will receive the is-animated class. Otherwise, it will lose this class and become hidden.

Without going into too much detail, here’s the JavaScript code that implements this functionality:

Note1: To see how this API works and what it returns as you scroll, print in the browser console the entry.

Note2: Initially, instead of the intersectionRatio property I tried the isIntersecting one. However, I noticed that it’s buggy in Firefox.

Tip: If you prefer the animation to run only once, you have to call the unobserve() method, like this:

And the associated CSS class:

Conclusion

That’s it, folks! Today, we managed to build an interesting scroll effect by taking advantage of exciting new CSS and JavaScript features. 

Obviously, like any modern tool, this effect has some limitations regarding browser support, especially if you target older browsers. For example, as a fallback, you can show the colored images by default in case the browser doesn’t support the clip-path property. 

Hopefully though, this exercise helped you learn something new and has given you some creative ideas. Here’s a reminder of what we built:

As always, thanks a lot for reading!

Next Level Front-End Learning

When you’re learning anything to do with front-end, the key is practice! The following tutorials will teach you a variety of techniques to add to your CSS and JavaScript toolkit:

Leave a comment

Your email address will not be published.