20 CSS Animated Buttons 03 / 20

CSS Pure CSS Ripple Effect Button

A Material Design-inspired ripple effect on button click, achieved entirely with CSS pseudo-elements and the :active state — no JavaScript required.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="ab-03">
  <p class="ab-03__label">Click or tap to trigger</p>
  <div class="ab-03__row">
    <button class="ab-03__btn ab-03__btn--violet">Get Started</button>
    <button class="ab-03__btn ab-03__btn--teal">Learn More</button>
    <button class="ab-03__btn ab-03__btn--rose">Subscribe</button>
  </div>
</div>
.ab-03,.ab-03 *,.ab-03 *::before,.ab-03 *::after{box-sizing:border-box;margin:0;padding:0}
.ab-03 ::selection{background:#7c3aed;color:#fff}
.ab-03{
  font-family:system-ui,sans-serif;
  background:#0f0c1a;
  min-height:100vh;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:2rem;
  padding:2rem;
}
.ab-03__label{
  font-size:.78rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:#a78bfa;
  opacity:.6;
}
.ab-03__row{
  display:flex;
  gap:1rem;
  flex-wrap:wrap;
  justify-content:center;
}
.ab-03__btn{
  position:relative;
  overflow:hidden;
  padding:.85rem 2rem;
  font-size:.95rem;
  font-weight:700;
  color:#fff;
  border:none;
  border-radius:10px;
  cursor:pointer;
  outline:none;
  letter-spacing:.03em;
  transition:filter .2s;
}
.ab-03__btn:hover{filter:brightness(1.1)}
.ab-03__btn:active{filter:brightness(.9)}
.ab-03__btn--violet{background:#7c3aed}
.ab-03__btn--teal{background:#0d9488}
.ab-03__btn--rose{background:#e11d48}
.ab-03__btn::after{
  content:'';
  position:absolute;
  top:50%;left:50%;
  width:0;height:0;
  background:rgba(255,255,255,.4);
  border-radius:50%;
  transform:translate(-50%,-50%);
  pointer-events:none;
  opacity:0;
}
.ab-03__btn:active::after{
  animation:ab-03-ripple .6s linear forwards;
}
@keyframes ab-03-ripple{
  0%{width:0;height:0;opacity:.4}
  80%{width:400%;height:400%;opacity:.1}
  100%{width:500%;height:500%;opacity:0}
}
@media(prefers-reduced-motion:reduce){
  .ab-03__btn:active::after{animation:none}
}

How this works

The ripple lives in the ::after pseudo-element, which starts as a small circle at the button centre using border-radius: 50% and width/height: 0. On :active, a @keyframes ab-03-ripple animation scales the circle to 400% of the button width while fading opacity from 0.35 to 0 — the classic expanding ring illusion. Centering the pseudo-element with top: 50%; left: 50%; transform: translate(-50%,-50%) keeps the ripple origin consistent regardless of button dimensions.

Because the animation only fires on :active, it naturally resets when the user releases the pointer. overflow: hidden on the parent clips the expanding disk, while pointer-events: none on the pseudo-element prevents it from interfering with the click target. The entire effect uses only transform, opacity, and width/height inside the keyframe, with only the transform path on the compositor — width/height changes trigger layout but are brief and isolated to the pseudo-element.

Customize

  • Reposition the ripple origin to a corner by changing the top/left percentages on ::after — e.g. top: 80%; left: 10% for a bottom-left burst.
  • Control ripple speed by editing the animation-duration on .ab-03__btn:active::after (default .6s; try .4s for snappy feel).
  • Change the ripple colour by updating the background on .ab-03__btn::after (default rgba(255,255,255,.45)).
  • Add a fill-colour variant by appending a second keyframe that changes the button background on active, creating a ripple-plus-fill combo.
  • Give dark-coloured buttons a dark ripple by switching the pseudo-element background to rgba(0,0,0,.12) so it reads on light fill colours.

Watch out for

  • The pure-CSS ripple always originates from the element centre — it cannot track where the user clicked. Use a JS-powered version if accurate origin matters for the design.
  • On iOS Safari, :active only fires if the element has a cursor: pointer or a touch event listener — add cursor:pointer to the button to ensure activation on mobile.
  • If the button has border-radius > 50% (a pill shape), the escaping corners of the growing ::after circle must be clipped with overflow: hidden on the button.

Browser support

ChromeSafariFirefoxEdge
26+ 7+ 16+ 26+

The :active pseudo-class and CSS animations are universally supported across all modern browsers.

Search CodeFronts

Loading…