Pure CSS ripple with minimal effort
This post describes how a simplified ripple effect can be achieved with minimal CSS and without using pseudo elements. If you are interested in enhancing the UX of your web app a little bit with close to zero effort keep reading!
Click the button to see it ripple:
❗️Also, I have a collection of 12 Pure CSS Material Components.
The ripple effect
The ripple effect is the phenomenon of dropping something like a stone into still water. In loose interpretation of Material Design this is mimicked with a circular highlight that is fading while growing from zero to maximum radius. This effect is triggered by touch and click events:
Connect user input to surface reactions with touch ripples.
The idea
The circle shape can be created with a simple radial-gradient
. Gradients are not animatable, so the circle cannot be transitioned into another radial-gradient
to make it grow. However, background-size
is animatable, so defining a circle with a radius of 1%
for example, then transitioning the background-size
makes the circle size transition.
The other part of the effect is the fade of the highlight. The background-color
property is also animatable, so it can be used to implement the fading transition. The transparent parts of the background-image
reveal the background-color
since the image is drawn atop of the color. Therefore the circle has to be defined transparent in the gradient to make it look like it is fading.
This implementation of button with ripple has three states: normal, :hover
and :active
. The ripple is triggered when the state changes from active to hover, that is when the click or tap is released. The last detail is that the area around the circle should be filled with the color of the :hover
state.
Human readable states
Maybe it is easier to understand the implementation when described less technically.
In normal state no gradient is displayed, only the solid color of the background is visible.
The hover state changes the color of the background to the hover color (a lighter one), and adds a gradient. The gradient defines a small transparent circle in the middle, and the rest is filled with the hover color. The size of the background is increased so the size of the circle is actually larger than the button.
Finally the active state changes the color of the background to the active color (an even lighter one), and reduces the size of the background to normal. This is perceived as the button is solidly filled with the hover color, except that there is a small lighter dot in the middle.
The trick is that the transitions only happen when the state changes from active to hover. They make the small dot in the middle grow to the full size of the button, and darken its color from the active to hover color.
Limitations
One very obvious limitation is that the ripple will always start from the center of the button. The true ripple effect starts from the point of the interaction, where the user clicks or taps. This would need the tracking of mouse or touch coordinates which is not possible in CSS.
Another constraint is that background-size in Internet Explorer is not animatable (in Edge it is!). So in IE it gracefully degrades to full button highlight in active state.
The takeway: snippets
In CSS or PostCSS:
Replace var(--focus-color) and var(--active-color) strings with your actual color values respectfully if you are not using a preprocessor.
.ripple {
background-position: center;
transition: background 0.5s;
}
.ripple:hover {
background: var(--focus-color) radial-gradient(circle, transparent 1%, var(--focus-color) 1%) center/15000%;
}
.ripple:active {
background-color: var(--active-color);
background-size: 100%;
transition: background 0s;
}
In LESS:
.ripple {
background-position: center;
transition: background 0.5s;
&:hover {
background: @focus-color radial-gradient(circle, transparent 1%, @focus-color 1%) center/15000%;
}
&:active {
background-color: @active-color;
background-size: 100%;
transition: background 0s;
}
}
In SCSS:
.ripple {
background-position: center;
transition: background 0.5s;
&:hover {
background: $focus-color radial-gradient(circle, transparent 1%, $focus-color 1%) center/15000%;
}
&:active {
background-color: $active-color;
background-size: 100%;
transition: background 0s;
}
}
Thanks for reading, feel free to let this post ripple through the web!