36 Tailwind CSS Button Hover Effects
28 / 36

All 36 in 36 Tailwind CSS Button Hover Effects

  1. 01Tailwind Magnetic Ripple Attraction Button Hover Effect
  2. 02Tailwind Cyberpunk Holographic Glitch Grid Button
  3. 03Tailwind Frosted Glassmorphism Shimmer Glow Button
  4. 04Tailwind Organic Blob Morph Button Hover Effect
  5. 05Tailwind Dual-Line Intersection Border Draw Button
  6. 06Tailwind Isometric Tactile 3D Depress Button
  7. 07Tailwind Infinite Sliding Kinetic Marquee Button
  8. 08Tailwind Chroma Conic Gradient Border Spin Button
  9. 09Tailwind Minimalist Border & Outline Drawing Hover Effects
  10. 10Tailwind Modern 3D Skeuomorphic Button Press Effects
  11. 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
  12. 12Tailwind Sliding Background Color Wipe Button Effects
  13. 13Tailwind Liquid Fill & Organic Morphing Button Effects
  14. 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
  15. 15Tailwind Liquid Wave Fill Button Hover Effect
  16. 16Tailwind Magnetic Border Drift Button
  17. 17Tailwind Glitch Slice Button Hover Effect
  18. 18Tailwind Neon Pulse Glow Button Hover Effect
  19. 19Tailwind Split Reveal Curtain Button Effect
  20. 20Tailwind Diagonal Shutter Sweep Button Effect
  21. 21Tailwind Elastic Lift Spring Button Hover Effect
  22. 22Tailwind Center Underline Draw Link Button Effects
  23. 23Tailwind 3D Flip Card Button Hover Effect
  24. 24Tailwind Particle Burst Confetti Button Effect
  25. 25Tailwind Slide Doors Closing Panel Button Effect
  26. 26Tailwind Typewriter Retype Terminal Button Effect
  27. 27Tailwind Hard Shadow Shift Flat Button Effect
  28. 28Tailwind Material Ripple Wave Button Effect
  29. 29Tailwind Rainbow Gradient Border Spin Button
  30. 30Tailwind Morph Shape Silhouette Button Effects
  31. 31Tailwind Icon Slide In Directional Button Effect
  32. 32Tailwind Cursor Spotlight Glow Button
  33. 33Tailwind Stagger Fill Equaliser Button Effect
  34. 34Tailwind Sunburst Expand Radiant Button Effect
  35. 35Tailwind 3D Press Plunger Key Button Effect
  36. 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Pure TailwindMIT licensed

Tailwind Material Ripple Wave Button Effect

A Material-style ripple: on hover a circular wave expands from the centre and fades as it grows, like a drop hitting water. No pointer tracking, so unlike entry 01 this one needs no JavaScript at all — and design B turns the same two properties into a repeating sonar echo.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<div class="tbh-28-group w-full min-h-svh flex flex-wrap">
<section class="tbh-28a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh28-sans bg-tbh28a-stage" aria-label="Ripple wave button">
  <button type="button" class="relative isolate overflow-hidden cursor-pointer border-0 rounded-full px-11 py-[17px] text-base font-extrabold text-white bg-tbh28a-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px] before:content-[''] before:absolute before:left-1/2 before:top-1/2 before:-ml-[60px] before:-mt-[60px] before:w-[120px] before:h-[120px] before:z-0 before:rounded-full before:bg-white/40 before:scale-0 before:opacity-100 before:transition-[scale,opacity] before:duration-600 before:ease-out hover:before:scale-[4] hover:before:opacity-0 focus-visible:before:scale-[4] focus-visible:before:opacity-0 motion-reduce:before:transition-opacity motion-reduce:hover:before:scale-0 motion-reduce:hover:before:opacity-30">
    <span class="relative z-[1]">Continue</span>
  </button>
</section>
<section class="tbh-28b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh28-sans bg-tbh28b-stage" aria-label="Echo ripple button">
  <button type="button" class="relative cursor-pointer border-0 rounded-full px-11 py-[17px] text-base font-extrabold text-white bg-tbh28b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 before:content-[''] before:absolute before:inset-0 before:rounded-full before:border-2 before:border-tbh28b-acc before:opacity-0 before:pointer-events-none after:content-[''] after:absolute after:inset-0 after:rounded-full after:border-2 after:border-tbh28b-acc after:opacity-0 after:pointer-events-none hover:before:animate-tbh28b-echo hover:after:animate-tbh28b-echo hover:after:[animation-delay:.7s] focus-visible:before:animate-tbh28b-echo focus-visible:after:animate-tbh28b-echo focus-visible:after:[animation-delay:.7s] motion-reduce:hover:before:animate-none motion-reduce:hover:after:animate-none">
    <span class="relative z-[1]">Ping</span>
  </button>
</section>
</div>
@import "tailwindcss";

@theme {
  --color-tbh28a-acc: #3d93a8;
  --color-tbh28a-acc: oklch(0.58 0.16 200);
  --color-tbh28a-stage: #081418;
  --color-tbh28a-stage: oklch(0.14 0.02 200);
  --color-tbh28b-acc: #5c7ce0;
  --color-tbh28b-acc: oklch(0.6 0.17 260);
  --color-tbh28b-stage: #0a0c1a;
  --color-tbh28b-stage: oklch(0.13 0.02 265);
  --font-tbh28-sans: 'Space Grotesk', system-ui, sans-serif;
  --animate-tbh28b-echo: tbh28b-echo 1.4s ease-out infinite;
}
/* Stays CSS: the echo keyframe. Design A needs none — a transition does the ripple, and it
   resets on pointer-out for free. */

@keyframes tbh28b-echo {
  0% {
    opacity: 0.7;
    scale: 1;
  }

  100% {
    opacity: 0;
    scale: 1.5;
  }
}

.tbh-28-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-28a,
.tbh-28b {
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tbh28a-acc: #3d93a8;
  --color-tbh28a-acc: oklch(0.58 0.16 200);
  --color-tbh28a-stage: #081418;
  --color-tbh28a-stage: oklch(0.14 0.02 200);
  --color-tbh28b-acc: #5c7ce0;
  --color-tbh28b-acc: oklch(0.6 0.17 260);
  --color-tbh28b-stage: #0a0c1a;
  --color-tbh28b-stage: oklch(0.13 0.02 265);
  --font-tbh28-sans: 'Space Grotesk', system-ui, sans-serif;
  --animate-tbh28b-echo: tbh28b-echo 1.4s ease-out infinite;
}
/* Stays CSS: the echo keyframe. Design A needs none — a transition does the ripple, and it
   resets on pointer-out for free. */

@keyframes tbh28b-echo {
  0% {
    opacity: 0.7;
    scale: 1;
  }

  100% {
    opacity: 0;
    scale: 1.5;
  }
}

.tbh-28-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-28a,
.tbh-28b {
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • Read against entry 01: same visual idiom, and the difference is exactly whether the ripple originates at the pointer. Centre-origin needs no script at all, which is worth knowing before adding a pointer handler to every CTA in a product.
  • hover:after:[animation-delay:.7s] is the whole of design B's echo, and it has to be an arbitrary property because Tailwind (correctly) has no delay scale for animations — a half-cycle offset is a value derived from the duration, not a design token.
  • UI change from the CSS original: both ripples fire on focus-visible:, and design A's reduced-motion state leaves a static 30% tint instead of a fully invisible layer, so hover is still acknowledged.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Material Ripple Wave Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-material-ripple-wave-button-effect/

A Material-style ripple: on hover a circular wave expands from the centre and fades as it grows, like a drop hitting water. No pointer tracking, so unlike entry 01 this one needs no JavaScript at all &mdash; and design B turns the same two properties into a repeating sonar echo.
## HTML
```html
<div class="tbh-28-group w-full min-h-svh flex flex-wrap">
<section class="tbh-28a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh28-sans bg-tbh28a-stage" aria-label="Ripple wave button">
  <button type="button" class="relative isolate overflow-hidden cursor-pointer border-0 rounded-full px-11 py-[17px] text-base font-extrabold text-white bg-tbh28a-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-[3px] before:content-[''] before:absolute before:left-1/2 before:top-1/2 before:-ml-[60px] before:-mt-[60px] before:w-[120px] before:h-[120px] before:z-0 before:rounded-full before:bg-white/40 before:scale-0 before:opacity-100 before:transition-[scale,opacity] before:duration-600 before:ease-out hover:before:scale-[4] hover:before:opacity-0 focus-visible:before:scale-[4] focus-visible:before:opacity-0 motion-reduce:before:transition-opacity motion-reduce:hover:before:scale-0 motion-reduce:hover:before:opacity-30">
    <span class="relative z-[1]">Continue</span>
  </button>
</section>
<section class="tbh-28b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh28-sans bg-tbh28b-stage" aria-label="Echo ripple button">
  <button type="button" class="relative cursor-pointer border-0 rounded-full px-11 py-[17px] text-base font-extrabold text-white bg-tbh28b-acc focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 before:content-[''] before:absolute before:inset-0 before:rounded-full before:border-2 before:border-tbh28b-acc before:opacity-0 before:pointer-events-none after:content-[''] after:absolute after:inset-0 after:rounded-full after:border-2 after:border-tbh28b-acc after:opacity-0 after:pointer-events-none hover:before:animate-tbh28b-echo hover:after:animate-tbh28b-echo hover:after:[animation-delay:.7s] focus-visible:before:animate-tbh28b-echo focus-visible:after:animate-tbh28b-echo focus-visible:after:[animation-delay:.7s] motion-reduce:hover:before:animate-none motion-reduce:hover:after:animate-none">
    <span class="relative z-[1]">Ping</span>
  </button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tbh28a-acc: #3d93a8;
  --color-tbh28a-acc: oklch(0.58 0.16 200);
  --color-tbh28a-stage: #081418;
  --color-tbh28a-stage: oklch(0.14 0.02 200);
  --color-tbh28b-acc: #5c7ce0;
  --color-tbh28b-acc: oklch(0.6 0.17 260);
  --color-tbh28b-stage: #0a0c1a;
  --color-tbh28b-stage: oklch(0.13 0.02 265);
  --font-tbh28-sans: 'Space Grotesk', system-ui, sans-serif;
  --animate-tbh28b-echo: tbh28b-echo 1.4s ease-out infinite;
}
/* Stays CSS: the echo keyframe. Design A needs none — a transition does the ripple, and it
   resets on pointer-out for free. */

@keyframes tbh28b-echo {
  0% {
    opacity: 0.7;
    scale: 1;
  }

  100% {
    opacity: 0;
    scale: 1.5;
  }
}

.tbh-28-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-28a,
.tbh-28b {
  min-height: 100svh;
}
```

How this works

Scale and opacity, nothing else. Both are compositor properties, so the ripple is effectively free no matter how large it grows. The circle starts at scale-0 fully opaque and ends at scale-[4] fully transparent; the eye reads that as a wave losing energy.

It resets by leaving. Because the effect is a transition rather than an animation, moving the cursor away returns the circle to scale-0 and full opacity — ready to fire again. An animation would need a reset trick to replay.

Design B is the same pair, looped. Two bordered rings scaling 1 → 1.5 while fading, the second delayed by half the cycle. That is a sonar ping rather than a ripple, and it is the version to use when the button needs to attract attention rather than confirm a hover.

Make it yours

  • Reach: hover:before:scale-[4] — enough to clear a wide button; raise it for very wide CTAs.
  • Ripple tint: before:bg-white/40. On light fills use a dark ripple at lower alpha instead.
  • Pace: before:duration-600 before:ease-out, the Material default feel.
  • Click-triggered: move the utilities from hover:before: to active:before: for press feedback rather than hover feedback.

Gotchas — read before shipping

  • overflow-hidden on design A clips the growing circle to the pill — without it the ripple escapes as a huge translucent disc.
  • Keep the ripple below the label (before:z-0 under a relative z-[1] span), or the text dims as the wave passes.
  • Design B must NOT clip (its rings expand outside the button), so it has no overflow-hidden — the two designs are deliberately opposite on that point.
  • A hover-triggered infinite animation (design B) keeps running as long as the cursor rests on the button; that is intended here, but never combine it with a box-shadow animation, which would repaint every frame.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Transforms, opacity and animation-delay are universal; with CSS unavailable each button is a plain solid CTA.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Arbitraryneeds bracket syntax

Plain CSSno Tailwind surface

Search CodeFronts

Loading…