I recently had to implement the ripple effect from material design into a web application. And then I realized I had no idea how that was implemented. This led me into a journey to study existing implementations, and even come up with a brand new technique that might be useful to you.
What is this ripple effect?
Wait, you don’t know the ripple effect from Google’s Material Design? Have you been living on a cave for how many years?
The ripple effect is used when you press a button. It works the same way for mouse or touch interactions.
The position you click or touch on the button is called the point of contact. From there, a ripple is sent moving outwards, losing opacity as it grows larger until it fills the entire button. Then it disappears completely.
The dynamics of this ripple effect are similar to the ripples you get when you touch a liquid surface, or when you drop a rock into a lake.
The ripples you will find on the web
After doing some research, I could find two main techniques that are used for implementing the ripple effect on web applications.
Using ::after pseudo-element
Using this technique, the
::after pseudo-element of the button is styled as a semi-transparent circle, and animated to grow and fade. The container button needs to have
overflow: hidden so that the circle never overflows outside of the button’s surface, and
position: relative to make it easy to position the circle within the button. You can read more details of this technique on this article by Ionuț Colceriu.
One of the great things about this technique is that it’s a pure CSS solution to the ripple effect. However, the ripple effect always starts from the center of the button, instead of the point of contact. That’s not the most natural feedback.
::after pseudo-element uses these variables for positioning.
Using child elements
In essence, this technique uses the same strategy as before. But instead of a pseudo-element, it adds a
The simplest implementation creates a
span for each click on the button, and uses the mouse position on the click event to change the position of the
span. A CSS animation makes the
span grow and fade until becoming fully transparent. We can choose to remove the
span from the DOM once the animation finishes, or just leave it there under the carpet — no one will really notice a transparent
span hanging around.
I did find another variation of this, on which the child element is an
svg instead of a
span, and the
A problem with submit inputs
Both techniques described above seem great. But this is what happens when I tried to apply them on
input elements with
Why don’t they work?
input element is a replaced element. In short, that means that there’s very little you can do with those elements, with respect to DOM and CSS. Specifically, they can’t have child elements, and no pseudo-elements either. Now it’s clear why these techniques fail.
So, if you’re using Material Design, it’s better to stay away from
input[type=submit], and stick to
button elements. Or just keep reading.
Adding ripples to submit inputs
Using a wrapping container
I quickly realized that I could wrap the submit button inside of an inline-block element, and use the inline-block element as the ripple surface. Here’s a quick demo:
While I do like this solution for its simplicity, it still required me to change the markup in too many places. And I knew it would be a fragile solution — new developers would come into the project, and create submit buttons without properly wrapping them in a ripple surface. So I kept searching for other solutions that didn’t require changing the DOM.
radial-gradient syntax allows me to control both the center and the size of the gradient. Of course, it also allows me to control the color of the gradient, including semi-transparent colors. And it never ever overflows the element it’s applied to. So it seems like it already does everything I need!
Not so fast… there is one thing missing: the
background-image property is not animatable. I could not make the gradient grow and fade to transparent using CSS animations. I did manage to make it grow by animating the
background-size property, but that was all I could do.
I tried a few other things, such as having a fading circle as an animated image (using the apng format), and applied it as a
background-image. But then I could not control when the image loop started and ended.
I started with the radial gradient solution above, and used
window.requestAnimationFrame to make a fluid animation of the radial gradient, growing and fading. Here’s my final solution:
So it is possible to have ripple effects on submit buttons, just not with CSS alone.
I couldn’t find this technique documented anywhere in the web, so I’m calling it my own. Leonardo’s ripple technique does not require changes to DOM, and works for any element because it doesn’t rely on pseudo-elements or child elements. However, it’s not a flawless solution.
background-image, I would suspect that browsers would not need to reflow, and would just require reapplying the styles and repainting the element. In practice, that is exactly what happens, and performance is really good. The exception to that statement is Firefox Mobile, which for some reason does not keep up with the animation. (edit: animation is smooth on modern Firefox Mobile versions)
Second, the technique uses the
Third, this does not seem to work in Internet Explorer. However, I don’t see any reason why it shouldn’t work with IE10 and above. Maybe it’s because IE uses a different syntax for
radial-gradient. But, who cares about IE now-days? (edit: this method works without any issues on Internet Explorer 11).
Head of Delivery
Connect with Leonardo via LinkedIn