21 / 36
All 36 in 36 Tailwind CSS Button Hover Effects
- 01Tailwind Magnetic Ripple Attraction Button Hover Effect
- 02Tailwind Cyberpunk Holographic Glitch Grid Button
- 03Tailwind Frosted Glassmorphism Shimmer Glow Button
- 04Tailwind Organic Blob Morph Button Hover Effect
- 05Tailwind Dual-Line Intersection Border Draw Button
- 06Tailwind Isometric Tactile 3D Depress Button
- 07Tailwind Infinite Sliding Kinetic Marquee Button
- 08Tailwind Chroma Conic Gradient Border Spin Button
- 09Tailwind Minimalist Border & Outline Drawing Hover Effects
- 10Tailwind Modern 3D Skeuomorphic Button Press Effects
- 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
- 12Tailwind Sliding Background Color Wipe Button Effects
- 13Tailwind Liquid Fill & Organic Morphing Button Effects
- 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
- 15Tailwind Liquid Wave Fill Button Hover Effect
- 16Tailwind Magnetic Border Drift Button
- 17Tailwind Glitch Slice Button Hover Effect
- 18Tailwind Neon Pulse Glow Button Hover Effect
- 19Tailwind Split Reveal Curtain Button Effect
- 20Tailwind Diagonal Shutter Sweep Button Effect
- 21Tailwind Elastic Lift Spring Button Hover Effect
- 22Tailwind Center Underline Draw Link Button Effects
- 23Tailwind 3D Flip Card Button Hover Effect
- 24Tailwind Particle Burst Confetti Button Effect
- 25Tailwind Slide Doors Closing Panel Button Effect
- 26Tailwind Typewriter Retype Terminal Button Effect
- 27Tailwind Hard Shadow Shift Flat Button Effect
- 28Tailwind Material Ripple Wave Button Effect
- 29Tailwind Rainbow Gradient Border Spin Button
- 30Tailwind Morph Shape Silhouette Button Effects
- 31Tailwind Icon Slide In Directional Button Effect
- 32Tailwind Cursor Spotlight Glow Button
- 33Tailwind Stagger Fill Equaliser Button Effect
- 34Tailwind Sunburst Expand Radiant Button Effect
- 35Tailwind 3D Press Plunger Key Button Effect
- 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Tailwind Elastic Lift Spring Button Hover Effect
A bouncy, delightful CTA that springs up on hover — it lifts, squashes slightly and overshoots before settling. The squash is the part most implementations skip, and in v4 it is one utility (hover:scale-x-[1.04] hover:scale-y-[.96]) that no longer has to repeat the lift.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-21-group w-full min-h-svh flex flex-wrap">
<section class="tbh-21a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21a-stage" aria-label="Elastic lift button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21a-acc shadow-[0_8px_16px_-8px_color-mix(in_oklab,var(--color-tbh21a-acc)_70%,transparent)] transition-[translate,scale,box-shadow] duration-[450ms] ease-[cubic-bezier(.68,-.6,.32,1.6)] hover:-translate-y-1.5 hover:scale-x-[1.04] hover:scale-y-[.96] hover:shadow-[0_22px_30px_-12px_color-mix(in_oklab,var(--color-tbh21a-acc)_65%,transparent)] focus-visible:-translate-y-1.5 focus-visible:scale-x-[1.04] focus-visible:scale-y-[.96] active:-translate-y-px active:scale-[.97] active:duration-[120ms] focus-visible:outline-3 focus-visible:outline-tbh21a-acc focus-visible:outline-offset-4 motion-reduce:transition-[box-shadow] motion-reduce:duration-200 motion-reduce:hover:-translate-y-[3px] motion-reduce:hover:scale-100 motion-reduce:focus-visible:scale-100">
Join the fun
</button>
</section>
<section class="tbh-21b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21b-stage" aria-label="Wobble tilt button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21b-acc shadow-[0_10px_20px_-10px_var(--color-tbh21b-acc)] transition-[scale] duration-200 hover:animate-tbh21b-wob focus-visible:animate-tbh21b-wob active:scale-[.96] focus-visible:outline-3 focus-visible:outline-tbh21b-acc focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.03]">
Say cheese
</button>
</section>
</div><div class="tbh-21-group w-full min-h-svh flex flex-wrap">
<section class="tbh-21a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21a-stage" aria-label="Elastic lift button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21a-acc shadow-[0_8px_16px_-8px_color-mix(in_oklab,var(--color-tbh21a-acc)_70%,transparent)] transition-[translate,scale,box-shadow] duration-[450ms] ease-[cubic-bezier(.68,-.6,.32,1.6)] hover:-translate-y-1.5 hover:scale-x-[1.04] hover:scale-y-[.96] hover:shadow-[0_22px_30px_-12px_color-mix(in_oklab,var(--color-tbh21a-acc)_65%,transparent)] focus-visible:-translate-y-1.5 focus-visible:scale-x-[1.04] focus-visible:scale-y-[.96] active:-translate-y-px active:scale-[.97] active:duration-[120ms] focus-visible:outline-3 focus-visible:outline-tbh21a-acc focus-visible:outline-offset-4 motion-reduce:transition-[box-shadow] motion-reduce:duration-200 motion-reduce:hover:-translate-y-[3px] motion-reduce:hover:scale-100 motion-reduce:focus-visible:scale-100">
Join the fun
</button>
</section>
<section class="tbh-21b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21b-stage" aria-label="Wobble tilt button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21b-acc shadow-[0_10px_20px_-10px_var(--color-tbh21b-acc)] transition-[scale] duration-200 hover:animate-tbh21b-wob focus-visible:animate-tbh21b-wob active:scale-[.96] focus-visible:outline-3 focus-visible:outline-tbh21b-acc focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.03]">
Say cheese
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}<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">
<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.
- The squash survives edits now.
translateY(-6px) scale(1.04,.96)was one value where changing the lift meant retyping the deformation;hover:-translate-y-1.5 hover:scale-x-[1.04] hover:scale-y-[.96]is three independent facts. active:duration-[120ms]is the port's small revelation: a per-state duration override is a utility, where the CSS version needed a separatetransition-durationdeclaration inside the:activerule.- UI change from the CSS original: both designs now spring on
focus-visible:, and design A's reduced-motion fallback keeps a 3px lift with no squash rather than dropping to a shadow change alone.
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 Elastic Lift Spring Button Hover Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-elastic-lift-spring-button-hover-effect/
A bouncy, delightful CTA that springs up on hover — it lifts, squashes slightly and overshoots before settling. The squash is the part most implementations skip, and in v4 it is one utility (hover:scale-x-[1.04] hover:scale-y-[.96]) that no longer has to repeat the lift.
## HTML
```html
<div class="tbh-21-group w-full min-h-svh flex flex-wrap">
<section class="tbh-21a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21a-stage" aria-label="Elastic lift button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21a-acc shadow-[0_8px_16px_-8px_color-mix(in_oklab,var(--color-tbh21a-acc)_70%,transparent)] transition-[translate,scale,box-shadow] duration-[450ms] ease-[cubic-bezier(.68,-.6,.32,1.6)] hover:-translate-y-1.5 hover:scale-x-[1.04] hover:scale-y-[.96] hover:shadow-[0_22px_30px_-12px_color-mix(in_oklab,var(--color-tbh21a-acc)_65%,transparent)] focus-visible:-translate-y-1.5 focus-visible:scale-x-[1.04] focus-visible:scale-y-[.96] active:-translate-y-px active:scale-[.97] active:duration-[120ms] focus-visible:outline-3 focus-visible:outline-tbh21a-acc focus-visible:outline-offset-4 motion-reduce:transition-[box-shadow] motion-reduce:duration-200 motion-reduce:hover:-translate-y-[3px] motion-reduce:hover:scale-100 motion-reduce:focus-visible:scale-100">
Join the fun
</button>
</section>
<section class="tbh-21b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21b-stage" aria-label="Wobble tilt button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21b-acc shadow-[0_10px_20px_-10px_var(--color-tbh21b-acc)] transition-[scale] duration-200 hover:animate-tbh21b-wob focus-visible:animate-tbh21b-wob active:scale-[.96] focus-visible:outline-3 focus-visible:outline-tbh21b-acc focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.03]">
Say cheese
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}
```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 Elastic Lift Spring Button Hover Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-elastic-lift-spring-button-hover-effect/
A bouncy, delightful CTA that springs up on hover — it lifts, squashes slightly and overshoots before settling. The squash is the part most implementations skip, and in v4 it is one utility (hover:scale-x-[1.04] hover:scale-y-[.96]) that no longer has to repeat the lift.
## HTML
```html
<div class="tbh-21-group w-full min-h-svh flex flex-wrap">
<section class="tbh-21a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21a-stage" aria-label="Elastic lift button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21a-acc shadow-[0_8px_16px_-8px_color-mix(in_oklab,var(--color-tbh21a-acc)_70%,transparent)] transition-[translate,scale,box-shadow] duration-[450ms] ease-[cubic-bezier(.68,-.6,.32,1.6)] hover:-translate-y-1.5 hover:scale-x-[1.04] hover:scale-y-[.96] hover:shadow-[0_22px_30px_-12px_color-mix(in_oklab,var(--color-tbh21a-acc)_65%,transparent)] focus-visible:-translate-y-1.5 focus-visible:scale-x-[1.04] focus-visible:scale-y-[.96] active:-translate-y-px active:scale-[.97] active:duration-[120ms] focus-visible:outline-3 focus-visible:outline-tbh21a-acc focus-visible:outline-offset-4 motion-reduce:transition-[box-shadow] motion-reduce:duration-200 motion-reduce:hover:-translate-y-[3px] motion-reduce:hover:scale-100 motion-reduce:focus-visible:scale-100">
Join the fun
</button>
</section>
<section class="tbh-21b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh21-sans bg-tbh21b-stage" aria-label="Wobble tilt button">
<button type="button" class="cursor-pointer border-0 rounded-[14px] px-[42px] py-[18px] text-[17px] font-extrabold text-white bg-tbh21b-acc shadow-[0_10px_20px_-10px_var(--color-tbh21b-acc)] transition-[scale] duration-200 hover:animate-tbh21b-wob focus-visible:animate-tbh21b-wob active:scale-[.96] focus-visible:outline-3 focus-visible:outline-tbh21b-acc focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.03]">
Say cheese
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh21a-acc: #ef8347;
--color-tbh21a-acc: oklch(0.68 0.19 40);
--color-tbh21a-stage: #fff5ec;
--color-tbh21a-stage: oklch(0.97 0.02 60);
--color-tbh21b-acc: #d966c4;
--color-tbh21b-acc: oklch(0.66 0.18 320);
--color-tbh21b-stage: #fbeffa;
--color-tbh21b-stage: oklch(0.96 0.02 320);
--font-tbh21-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh21b-wob: tbh21b-wob 0.5s cubic-bezier(.36,.07,.19,.97);
}
/* Stays CSS: the one-shot wobble. It writes rotate and scale as discrete properties, so it
composes with the button's own active:scale utility instead of overwriting it. */
@keyframes tbh21b-wob {
0% {
rotate: 0deg;
scale: 1;
}
25% {
rotate: -4deg;
scale: 1.05;
}
50% {
rotate: 3deg;
scale: 1.03;
}
75% {
rotate: -2deg;
scale: 1.02;
}
100% {
rotate: 0deg;
scale: 1;
}
}
.tbh-21-group {
width: 100%;
min-height: 100svh;
}
.tbh-21a,
.tbh-21b {
min-height: 100svh;
}
```How this works
Squash and stretch, cheaply. Scaling X up 4% and Y down 4% while lifting reads as mass compressing against the lift — the oldest animation trick there is. In CSS it required writing translateY(-6px) scale(1.04,.96) as one value, and any later edit to the lift risked losing the squash. Here they are three utilities that cannot interfere.
The curve does the work. cubic-bezier(.68,-.6,.32,1.6) dips below the start value before travelling and overshoots past the target before settling. That is why 6px of movement feels like a spring rather than a slide.
Press overrides the spring. active:duration-[120ms] is the detail that makes this usable: without it, clicking mid-bounce feels laggy because the press has to fight a 450ms curve.
Make it yours
- Bounce: the
cubic-bezier. Raise the final control point past 1.6 for cartoonish, flatten to 1.0 for corporate. - Lift height:
hover:-translate-y-1.5(6px). Beyond ~10px the shadow has to grow a lot to stay believable. - Squash amount: the 1.04 / .96 pair — keep them inverse, or the button appears to change size rather than deform.
- Design B's shake: the wobble keyframe's rotation stops (-4deg / 3deg / -2deg), a decaying sequence.
Gotchas — read before shipping
- Keep the lift and scale modest: a button that grows 10% clips its own shadow and overlaps neighbours in a button row.
- Grow the shadow WITH the lift (
hover:shadow-[0_22px_30px_-12px_...]), or the button reads as scaled rather than raised. - Override the duration on
active:. A springy curve on press feels broken. - Under
motion-reduce:the lift is halved to a plain 3px translate with no squash and no spring — still a response, no bounce.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
cubic-bezier and transforms are universal; nothing here needs a fallback beyond Tailwind v4's own floor.