09 / 20
All 20 in 20 Tailwind CSS Animated Buttons
- 01Tailwind Neon Glow Button with Pulsing Halo
- 02Tailwind Background Slide Hover Button (Four Directions)
- 03Tailwind Ripple Effect Button with No JavaScript
- 04Tailwind Animated Border Draw Button with Staged Delays
- 05Tailwind 3D Press Effect Button with Keycap Depth
- 06Tailwind Liquid Fill Button with Animated Wave Crest
- 07Tailwind Moving Gradient CTA Button
- 08Tailwind Shine Sweep Hover Button with Gloss Streak
- 09Tailwind Jelly Bounce Button with Elastic Squash
- 10Tailwind Text Flip Button Hover with 3D Rotation
- 11Tailwind Animated CTA Hero Button with Beacon Rings & Particle Burst
- 12Tailwind Submit Button Success State with Checkmark Draw
- 13Tailwind Animated Download Button with Progress Fill
- 14Tailwind Hamburger to Close Button with aria-expanded Variants
- 15Tailwind Add to Cart Button with Flying Product Dot
- 16Tailwind Social Share Buttons with Brand Hover & Copy Link
- 17Tailwind Animated Toggle Switch with peer-checked & has-checked
- 18Tailwind Loading Spinner Inside Button with State Machine
- 19Tailwind Next & Prev Arrow Buttons with Chasing Icons
- 20Tailwind Ghost Button with Cursor-Aware Background Reveal
Tailwind Jelly Bounce Button with Elastic Squash
Squash, stretch, and a settling wobble — seven keyframe steps of jelly physics. Nothing about the timeline can become a utility, and that is the point: the port keeps the keyframe verbatim and reduces the CSS to a single trigger, hover:animate-tab09-jelly.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tab-09 w-full min-h-svh grid place-items-center bg-tab09-bg font-tab09-sans px-6 py-10">
<div class="flex flex-col items-center gap-8 py-6">
<p class="text-[0.78rem] tracking-[0.14em] uppercase text-tab09-mut m-0">Hover for jelly effect</p>
<div class="flex flex-wrap justify-center gap-5">
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-pink rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-pink motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Subscribe!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-green rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-green motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Let's Go!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-sky rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-sky motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Join Now!</button>
</div>
</div>
</section><section class="tab-09 w-full min-h-svh grid place-items-center bg-tab09-bg font-tab09-sans px-6 py-10">
<div class="flex flex-col items-center gap-8 py-6">
<p class="text-[0.78rem] tracking-[0.14em] uppercase text-tab09-mut m-0">Hover for jelly effect</p>
<div class="flex flex-wrap justify-center gap-5">
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-pink rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-pink motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Subscribe!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-green rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-green motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Let's Go!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-sky rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-sky motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Join Now!</button>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}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-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}<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 keyframe uses the standalone
scaleproperty rather thantransform: scaleX() scaleY(), so it does not clobber other transform utilities on the same element — the v4-idiomatic form. - Nothing else about this demo ports: the timeline is the design, and it stays CSS. One class does the whole trigger.
- UI change from the CSS original: darker pink and green fills, because the originals ran white text at roughly 3:1 — below AA for a 17px label. The jelly is unchanged.
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 Jelly Bounce Button with Elastic Squash
Source: https://codefronts.com/motion/tailwind-css-animated-buttons/tailwind-jelly-bounce-button-animation/
Squash, stretch, and a settling wobble — seven keyframe steps of jelly physics. Nothing about the timeline can become a utility, and that is the point: the port keeps the keyframe verbatim and reduces the CSS to a single trigger, hover:animate-tab09-jelly.
## HTML
```html
<section class="tab-09 w-full min-h-svh grid place-items-center bg-tab09-bg font-tab09-sans px-6 py-10">
<div class="flex flex-col items-center gap-8 py-6">
<p class="text-[0.78rem] tracking-[0.14em] uppercase text-tab09-mut m-0">Hover for jelly effect</p>
<div class="flex flex-wrap justify-center gap-5">
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-pink rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-pink motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Subscribe!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-green rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-green motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Let's Go!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-sky rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-sky motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Join Now!</button>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}
```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 Jelly Bounce Button with Elastic Squash
Source: https://codefronts.com/motion/tailwind-css-animated-buttons/tailwind-jelly-bounce-button-animation/
Squash, stretch, and a settling wobble — seven keyframe steps of jelly physics. Nothing about the timeline can become a utility, and that is the point: the port keeps the keyframe verbatim and reduces the CSS to a single trigger, hover:animate-tab09-jelly.
## HTML
```html
<section class="tab-09 w-full min-h-svh grid place-items-center bg-tab09-bg font-tab09-sans px-6 py-10">
<div class="flex flex-col items-center gap-8 py-6">
<p class="text-[0.78rem] tracking-[0.14em] uppercase text-tab09-mut m-0">Hover for jelly effect</p>
<div class="flex flex-wrap justify-center gap-5">
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-pink rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-pink motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Subscribe!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-green rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-green motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Let's Go!</button>
<button type="button" class="min-h-11 px-9 text-[1.05rem] font-bold tracking-[0.03em] text-white bg-tab09-sky rounded-full cursor-pointer origin-center will-change-transform shadow-[0_4px_16px_oklch(0_0_0/0.15)] hover:animate-tab09-jelly focus-visible:animate-tab09-jelly focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tab09-sky motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:scale-[1.04]">Join Now!</button>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tab09-bg: #fdf2ff;
--color-tab09-bg: oklch(0.97 0.03 320);
--color-tab09-mut: #a855f7;
--color-tab09-mut: oklch(0.63 0.24 303);
--color-tab09-pink: #db2777;
--color-tab09-pink: oklch(0.59 0.22 354);
--color-tab09-green: #16a34a;
--color-tab09-green: oklch(0.58 0.16 149);
--color-tab09-sky: #0284c7;
--color-tab09-sky: oklch(0.56 0.15 240);
--font-tab09-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tab09-jelly: tab09-jelly 0.7s cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes tab09-jelly {
0% {
scale: 1 1;
}
10% {
scale: 1.2 0.85;
}
25% {
scale: 0.85 1.15;
}
40% {
scale: 1.1 0.92;
}
55% {
scale: 0.95 1.05;
}
70% {
scale: 1.03 0.98;
}
85% {
scale: 0.99 1.01;
}
100% {
scale: 1 1;
}
}
.tab-09 {
width: 100%;
min-height: 100svh;
}
.tab-09 ::selection {
background: var(--color-tab09-pink);
color: #fff;
}
```How this works
A timeline is not a design token. Seven steps of paired scale values have no utility form, nor should they — so the keyframe stays in CSS and only its binding becomes a class. This is the clearest case in the collection of Tailwind's actual division of labour.
Ending at identity is what makes it reversible. The last step is scale(1), so a cursor leaving mid-animation lands the button at its resting size. both in the token's fill-mode applies the first frame immediately, with no flash of the rest state.
Reduced motion still answers. A 4% static scale confirms the hover without any deformation — a different state, not a slower one.
Make it yours
- Intensity: push the extremes (
scaleX(1.35)squash,scaleY(1.2)stretch) in the keyframe. - Tighter settle: delete the 70% and 85% steps for a five-step wobble.
- Stagger a row by adding
[animation-delay:0.1s]/[animation-delay:0.2s]to successive buttons.
Gotchas — read before shipping
- Text can blur momentarily during the squash on low-DPI screens — a sub-pixel artefact of scaling rendered glyphs, not a bug you can fix in CSS.
- Inside a container with
overflow-hiddenthe stretch phase clips. Give the row padding. will-change-transformhelps here and hurts if you sprinkle it everywhere; it is justified because the animation is large, frequent and transform-only.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Multi-step transform keyframes are universally supported; the floor is Tailwind v4's generated sheet.