Create a button animation inspired by Google's Ripple Effect

I came across a website with a button that has a cool Google-like animation.

How can I create a similar button that triggers an animation when clicked?

Here is the code I have been working on:

button {
  text-transform: uppercase;
  padding: 0.8em;
  width: 100px;
  background: #0053d9;
  color: #fff;
  border: none;
  border-radius: 5px;
  transition: all 0.2s;
  font-size: 15px;
  font-weight: 500;
}

button:hover {
  filter: brightness(80%);
  cursor: pointer;
}

button:active {
  transform: scale(0.92)
}
<button>Login</button>

Answer №1

This particular animation effect is commonly referred to as the Material ripple effect, or at least that's how most people tend to identify it.

There are two distinct approaches to achieving this effect - one involves a combination of JS and CSS for a comprehensive ripple effect, where the ripple originates from the position of the mouse, while the other method relies solely on CSS without any JavaScript involved, resulting in the ripple emanating from the button regardless of the mouse's location within it.

While some individuals prefer the cleaner CSS-only approach, the majority lean towards the full-fledged version due to its consideration of mouse positioning, thereby delivering a slightly enhanced user experience...

In any case, I have developed both these effects, so feel free to choose the one that suits your preference :).

PS: Here are the guidelines applicable to any full-fledged versions you encounter:

  • The ripple should be triggered when the mouse is pressed down on the button - not upon clicking because mobile devices introduce an additional hundred milliseconds delay (as mobile browsers postpone delivering the click event to differentiate between single and double clicks). This delay before displaying the ripple significantly reduces the user experience, making your website appear slow and unresponsive even if it isn't actually sluggish.
  • The ripple must remain on the button, covering its background until the mouse is released, or the button loses focus - whichever occurs first.

Without further delay, here is the provided code snippet...

window.addEventListener("mousedown", e => {
    const target = e.target;

    if(target.nodeName == "BUTTON" && !target.classList.contains("css-only-ripple")) {
      show_ripple(target);
    }
  });
  function show_ripple(button) {
    const style = getComputedStyle(button);
    let ripple_elmnt = document.createElement("span");
    let diameter = Math.max(parseInt(style.height), parseInt(style.width)) * 1.5;
    let radius = diameter / 2;

    ripple_elmnt.className = "ripple";
    ripple_elmnt.style.height = ripple_elmnt.style.width = diameter + "px";
    ripple_elmnt.style.position = "absolute";
    ripple_elmnt.style.borderRadius = "1000px";
    ripple_elmnt.style.pointerEvents = "none";
    
    ripple_elmnt.style.left = event.clientX - button.offsetLeft - radius + "px";
    ripple_elmnt.style.top = event.clientY - button.offsetTop - radius + "px";

    ripple_elmnt.style.transform = "scale(0)";
    ripple_elmnt.style.transition = "transform 500ms ease, opacity 400ms ease";
    ripple_elmnt.style.background = "rgba(255,255,255,0.5)";
    button.appendChild(ripple_elmnt);

    setTimeout(() => {
      ripple_elmnt.style.transform = "scale(1)";
    }, 10);

    button.addEventListener("mouseup", e => {
      ripple_elmnt.style.opacity = 0;
      setTimeout(() => {
        try {
          button.removeChild(ripple_elmnt);
        } catch(er) {}
      }, 400);
    }, {once: true});
    button.addEventListener("blur", e => {
      ripple_elmnt.style.opacity = 0;
      setTimeout(() => {
        try {
          button.removeChild(ripple_elmnt);
        } catch(er) {}
      }, 450);
    }, {once: true});
  }
button {
      text-transform: uppercase;
      padding: 0.8em;
      width: 100px;
      background: #0053d9;
      color: #fff;
      border: none;
      border-radius: 5px;
      transition: all 0.2s;
      font-size: 15px;
      font-weight: 500;
      position: relative;
      overflow: hidden;
    }

    button:hover {
      filter: brightness(80%);
      cursor: pointer;
    }

    button:active {
      transform: scale(0.92)
    }

    .css-only-ripple::after {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      width: 150%;
      aspect-ratio: 1 / 1;
      transform: translate(-50%, -50%) scale(0);
      pointer-events: none;
      border-radius: 999px;
      background: rgba(255, 255, 255, .5);
    }

    .css-only-ripple:focus::after {
      animation: scale_up 1000ms forwards;
    }

    @keyframes scale_up {
      0% {
        transform: translate(-50%, -50%) scale(0);
        opacity: 1;
      }
      100% {
        transform: translate(-50%, -50%) scale(1);
        opacity: 0;
      }
    }
<button>Login</button>
<button class="css-only-ripple">Login</button>

<br>
The first button indicates the CSS and JS version, while the second presents the CSS-only variant. For the CSS-only button, remember to unfocus it before clicking again to display the ripple effect (only generated upon focus)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Customize stepper background color in Angular Material based on specific conditions

I am working on a project using Angular Material and I want to dynamically change the color of the stepper based on different states. For example: state: OK (should be green) state: REFUSED (should be red) Here is what I have done so far: <mat-horizo ...

CSS / JavaScript Navigation Menu overshadowing Flash content in Firefox

On my website, I have a drop-down menu that was created using CSS and JavaScript. This menu drops down over a Flash animation. Interestingly, in Internet Explorer 6 and 7, the drop-down menus successfully appear over the Flash animation. However, in Mozill ...

When incorporating Typescript into HTML, the text does not appear in bold as expected

Issue with bold functionality in Typescript Hey there, I am currently involved in an Angular project and I came across a problem with a function in a Typescript file that is supposed to bold a specific segment of text formatText() { ......... ...

Executing JavaScript function by clicking on <img>

I've been developing a website similar to YouTube, and I'm facing difficulties with the Like/Dislike feature for comments. Currently, I have implemented it in the following way: when a user clicks on an image (thumbsUp.png or thumbsDown.png), a ...

Encountering numerous challenges while attempting to create a switch theme button with the help of JavaScript's localStorage and CSS variables

It's frustrating that despite all my efforts, I'm still stuck on implementing a small feature for the past 5-6 hours. I need assistance with storing a single variable in the user's localStorage, initially set to 1. When the user clicks a but ...

Numerous titles being showcased

I need to enhance the functionality of clicking on multiple objects in order to display all titles at once within the header. Currently, only one title is shown each time an object is clicked. Therefore, when clicking on items 1, 2, and 3, I expect to se ...

CSS: The enigma of :nth-child - what eludes my understanding?

I have 2 divs which are similar to 2 td's in a table. My goal is to have 2 red divs, followed by 2 blue divs and so on. However, my current code doesn't seem to be working. Can someone point out what I am missing: <style> .irow :nth-child( ...

Randomly browsing with two clicks in jQuery

Attempting to achieve smooth scrolling on div tags by clicking anchor tags in a list using jQuery. enter code here function scrollThere(targetElement, speed) { // initiate an animation to a certain page element: $('html, body , # ...

Achieving Equal Spacing Between Containers without Using Grid or Flexbox

I'm struggling to evenly space four child containers within a single parent container without using Flex/Grid, as I haven't covered those topics yet. I tried setting each child container to 25% width of the parent and centering them with text-ali ...

Struggling to center the hidden divs both vertically and horizontally on the page when they are made visible

I'm having trouble aligning my hidden divs in the center of the page. When I set their position to relative, the scroll increases as I move from one page to another (meaning the div is not vertically centered on the page). Should I place all three d ...

Angular 5: ngFor Array issue with updating DOM view not resolved until 2nd click

Currently, I am utilizing the contact API to retrieve a list of contacts. contacts = new Array<Object>(); public signIn() { this.getContacts().then((res)=>{ console.log("res",res); this.contacts = res; }) } Below is the H ...

What is the best way to align my navigation links with my navbar?

In an effort to align my navigation links with the bottom of the navbar, I am working on positioning them below the navbar. Here is the desired layout: https://i.sstatic.net/eFL5r.png The goal is for the red section to start from the bottom of the navbar. ...

Struggling to incorporate a logo into the navigation bar for an HTML project

I'm a beginner in the world of HTML and CSS, trying my hand at creating a homepage. While I managed to add a logo to the Navigation bar, it seems to have messed up the link for the HOME page. Could this be due to not wrapping the logo in a div or imp ...

Is it appropriate for HTML5 Web Workers to utilize CORS for cross-origin requests?

As I was creating a hosted API that relies on web workers, I encountered an intriguing issue. I am looking for feedback from the community to help me with this. Even though my server has the necessary CORS headers in place to serve the worker JS files and ...

Tips for making my modal function properly?

I've encountered an issue with adding two modals to my website. The first modal works perfectly fine, but the second one is not functioning as expected. When I click on the button for the second modal, the background dims as it should, but the modal i ...

Validating your tel hyperlink with HTML5 W3C standards is essential for

Hey there! I have a question about adding telephone links to the numbers on my website. How can I ensure that it passes W3C validation successfully? Currently, when using this syntax: <a href="tel: 0141 123456">0141 123456</a> I am getting th ...

How can we target every nth child element universally?

I am working with an HTML structure that has a specific layout, and I need to target all odd <span> elements globally without considering their parent elements. Is there a way to style 1, 3, 5, 7, 9 in red color? I attempted to use :nth-child(odd) b ...

Leveraging node.js within website development

I have successfully loaded and hosted HTML using Node.js. My website, which consists of only HTML, CSS, and some frontend JavaScript, is fully developed. I do not plan on adding any additional frontend elements to it. I now want to utilize a Node.js serv ...

What is the best way to target the final n children using CSS?

Is there a way in CSS to target and style the last N elements of a parent container? The number of elements we want to match is not fixed, but could vary. How can this be achieved using CSS? ...

Toggle the visibility of a div based on the id found in JSON data

I am looking to implement a JavaScript snippet in my code that will show or hide a div based on the category ID returned by my JSON data. <div id="community-members-member-content-categories-container"> <div class="commun ...