Check out a free preview of the full Dynamic CSS with Custom Properties (aka CSS Variables) course

The "Dynamic Button Ripple Effect Solution" Lesson is part of the full, Dynamic CSS with Custom Properties (aka CSS Variables) course featured in this preview video. Here's what you'd learn in this lesson:

Lea walks through the solution to the dynamic button ripple effect exercise. A brief discussion on performance is also provided in this segment.


Transcript from the "Dynamic Button Ripple Effect Solution" Lesson

>> So let's first look at the effect we want to create before we set out to create it. So we want something like this, where we click and we have the same circle, but when we lift the mouse pointer, the circle grows in radius and decreases in opacity.

I've described it earlier, but this is what it looks like. So now let's go back and try to do that. So the radius of the circle is controlled through the position of the color stop. If we say 2em's, it's a much bigger circle. And we want it to basically start from 1em and go to ideally 100% so that we are certain that it covers our entire button.

If we just use a large number of em's, it could always end up needing more than that. So what we will do is we are going to use again a p custom property for the progression of the animation. And when the property is 0, we want to get 1em, when it's 1, we want to get 100%.

It's just another case of range mapping really. So let's define a property for, The radius, let's say ripple-radius. And let's use the same technique. So when p is 1, we want it to be 100%. And when p is 0, We want it to be 1em. And let's add these.

Okay, and let's see. So with 0, we get the circle that we already had. Let's do 0.2 and see what happens. See the circle is bigger. And let's do 0.5, even bigger. So it looks like it's working. Let's do 1, it should cover the entire thing. Fantastic, so this is already working for the circle.

Now, what else do we want to vary? We want to vary the opacity of the circle. So we start with 0.4 and we want to reach 0. So let's do ripple-opacity. Again, we're gonna use calc. So, When var p is 1, we want to get 0. And when p is 0, we want to get 0.4, right?

And we can actually remove this term because it's just multiplying by 0. Now let's try it again, 0 gives us the original circle, 0.2 gives us a larger circle. 0.6 gives us an even larger circle, but the opacity is not changing because I haven't actually applied the var here.

So, let's say ripple-opacity. Lovely, now that p is 0.6, we're getting a very faded out circle. Let's try 0.2 again, let's fad it out. Let's try 0. Okay, our original circle. So this works, but of course, we don't actually have an animation yet, because p is just hard-coded, it's not changing.

So what we really want to do is set p here, And when it's active, we want to get the 1em circle and the 0.4, and we get those when p is 0. So we wanna set it to 1 on the main rule and 0 on the active rule.

And then we wanna animate it, so we need to register it. So @property --p syntax: "<number>"; inherits, we don't care, we can just say false. And initial value is 0. Now we're gonna set a transition. Let's set it to one second and let's see what happens. These actually needs to be in the main rule, they shouldn't be applying only when the button is active, that's why we don't get anything when we lift the mouse pointer.

All we want here is the declaration that sets p to 0. So now, when we press our mouse pointer, we get this effect, and then when we lift it, we get the effect we wanted. So we're almost there, we're halfway there pretty much. We want the circle to start immediately when we get our mouse pointer down, but we do want the animation to happen when we lift our button up.

So how can we do that? This has to do with how transitions work. So the transition right now is applied on our main rule. So we get the same transition, the one second transition, both when we press the mouse button down and when we lift it up. If we only wanted one way, then we could go there and override transition duration, To be 0 seconds.

And let's try that. So you see now, the moment I press my mouse pointer down, I get the circle immediately. Because the moment I press my mouse pointer down, the active pseudo class activates, and the transition is 0, so I get no transition. However, once I'm in the active mode, once I lift my pointer up, then I'm not in active anymore, so this doesn't apply anymore.

So now I get the usual one second that I've specified in the base rule, so I actually get the transition now. And you can see how it works, it's basically variable we wanted. In Google's version, it's much faster, but we could do that. It's just kind of a shame because now we can't see it and we've spent all this work on it.

I know that some people might have questions about performance with these things, especially since we've used event delegation on document for many of these. Event delegation is when you monitor events on an ancestor, often even the entire document. So we monitor events on pretty much any element, even new elements that existed after we registered our event handler.

So that gives you maximum flexibility and maintainability, but it can be slower because we're monitoring everything. Sometimes, it makes sense to scope to specific elements that can be more performant, but it's a tighter coupling of CSS and JavaScript. Because you need to know which elements you're applying, which elements you will need to involve in your CSS beforehand.

I would say my personal recommendation would be for anything other than mouse move or pointer move, err on flexibility, it's not triggered that frequently. For mouse move or pointer move, it depends. I would say try to err on flexibility, measure the performance, adjust if needed. But in those, it's more reasonable to err on the side of having a tighter coupling to gain in performance.

Again, it depends on the website, it depends on how many DOM elements you have in general, how complex the website is. But mouse move can get quite slow if you're doing a lot of work there. So for events that are triggered very frequently, it makes sense to limit how many elements you're monitoring.

So in this case, we have used range mapping just to avoid registering a bunch of properties. We might have mentioned this before, it can be useful to avoid registering a ton of properties.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now