19 / 30
All 30 in 30 Tailwind CSS Text Hover Effects
- 01Tailwind CSS Animated Underline Hover Effect
- 02Tailwind Liquid Text Fill Hover Animation
- 03Tailwind Gradient Text Hover Effect with bg-clip-text
- 04Tailwind Animated Strikethrough Text Hover Effect
- 05Tailwind Text Swap and Flip Up Hover Effect
- 06Tailwind CSS Glitch Text Hover Effect
- 07Tailwind Neon Glow Text with Flicker Animation
- 08Tailwind Metallic Gold Shimmer Text Effect
- 09Tailwind 3D Text Pop and Lift Hover Effect
- 10Tailwind Staggered Letter Wave Hover Animation
- 11Tailwind Spotlight Text Reveal on Mouse Hover
- 12Tailwind Hover to Reveal Hidden Spoiler Text
- 13Tailwind Blur to Focus Text Hover Effect
- 14Tailwind Magnetic Hover Effect for Buttons and Text
- 15Tailwind Letter Spacing Expand on Hover (Tracking Animation)
- 16Tailwind Outline Text Stroke to Fill Hover Effect
- 17Tailwind Text Scramble and Matrix Decrypt Hover Effect
- 18Tailwind Animated Border Button Hover Effect
- 19Tailwind Elastic Rubber Band Text Hover Animation
- 20Tailwind Variable Font Weight Hover Animation
- 21Tailwind Animated Rainbow Gradient Text on Hover
- 22Tailwind 3D Perspective Tilt Text Hover Effect
- 23Tailwind Smoke Dissolve Text Hover Effect
- 24Tailwind Fire Text Effect with Heat Wave Glow
- 25Tailwind Dynamic Text Shadow that Follows the Cursor
- 26Tailwind Infinite Text Marquee that Speeds Up on Hover
- 27Tailwind Duotone Split Color Text Hover Effect
- 28Tailwind CSS Tooltip on Hover (No JavaScript)
- 29Tailwind SVG Water Ripple Distortion Text Effect
- 30Tailwind Clip-Path Text Reveal Hover Animation
Tailwind Elastic Rubber Band Text Hover Animation
A kids' coding-camp wordmark where every letter squashes and stretches like rubber, staggered 40ms and recoloured per letter. The whole animation is group-hover/word:*:animate-tth19-rubber on the parent with *:[animation-delay:calc(var(--i)*40ms)]; the CTA squashes on hover and presses with active:.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tth-19 w-full min-h-svh grid place-items-center bg-tth19-bg bg-[radial-gradient(85%_70%_at_50%_18%,var(--color-tth19-glow),var(--color-tth19-bg)_70%)] px-5 py-14 font-tth19-sans text-tth19-ink" aria-label="Elastic rubber band text demo">
<div class="flex flex-col items-center gap-[clamp(14px,2.8vw,24px)] text-center">
<p class="text-[13px] font-extrabold uppercase tracking-[0.2em] text-tth19-mut">After-school coding camp · ages 7–11</p>
<a id="tth19-top" href="#tth19-top" aria-label="WIGGLE CLUB" class="group/word flex flex-wrap justify-center gap-x-[0.35em] gap-y-[0.05em] rounded-xl px-[0.1em] py-[0.15em] font-tth19-display text-[clamp(54px,12.5vw,128px)] font-bold leading-none tracking-[-0.01em] no-underline focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tth19-acc">
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:0;--tth19-c:oklch(0.62 0.21 25)">W</i><i style="--i:1;--tth19-c:oklch(0.66 0.18 60)">I</i><i style="--i:2;--tth19-c:oklch(0.6 0.17 145)">G</i><i style="--i:3;--tth19-c:oklch(0.58 0.15 230)">G</i><i style="--i:4;--tth19-c:oklch(0.55 0.2 290)">L</i><i style="--i:5;--tth19-c:oklch(0.6 0.22 345)">E</i></span>
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:6;--tth19-c:oklch(0.66 0.18 60)">C</i><i style="--i:7;--tth19-c:oklch(0.6 0.17 145)">L</i><i style="--i:8;--tth19-c:oklch(0.58 0.15 230)">U</i><i style="--i:9;--tth19-c:oklch(0.55 0.2 290)">B</i></span>
</a>
<p class="max-w-[40ch] text-[15.5px] font-semibold leading-[1.6] text-tth19-mut">Blocks in term one, robots by term three. Snacks throughout.</p>
<a href="#tth19-top" class="group/btn mt-1.5 flex min-h-[58px] items-center justify-center gap-2 rounded-full bg-tth19-acc px-[34px] font-tth19-display text-[17px] font-bold tracking-[0.01em] text-white no-underline shadow-[0_6px_0_0_var(--color-tth19-acc-deep)] transition-[scale,box-shadow,translate] duration-450 ease-[cubic-bezier(.28,2.2,.4,1)] hover:scale-x-108 hover:scale-y-92 focus-visible:scale-x-108 focus-visible:scale-y-92 focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tth19-ink active:translate-y-1 active:scale-x-94 active:scale-y-104 active:shadow-[0_2px_0_0_var(--color-tth19-acc-deep)] active:duration-80 motion-reduce:transition-none">Save a spot<span aria-hidden="true" class="transition-transform duration-300 group-hover/btn:translate-x-1">→</span></a>
<p class="max-w-[56ch] text-[12.5px] leading-[1.7] text-tth19-mut">Hover the name (squash & stretch, staggered 40ms) — then press the button and feel it squash. Origin sits at the baseline so letters bounce off the floor.</p>
</div>
</section><section class="tth-19 w-full min-h-svh grid place-items-center bg-tth19-bg bg-[radial-gradient(85%_70%_at_50%_18%,var(--color-tth19-glow),var(--color-tth19-bg)_70%)] px-5 py-14 font-tth19-sans text-tth19-ink" aria-label="Elastic rubber band text demo">
<div class="flex flex-col items-center gap-[clamp(14px,2.8vw,24px)] text-center">
<p class="text-[13px] font-extrabold uppercase tracking-[0.2em] text-tth19-mut">After-school coding camp · ages 7–11</p>
<a id="tth19-top" href="#tth19-top" aria-label="WIGGLE CLUB" class="group/word flex flex-wrap justify-center gap-x-[0.35em] gap-y-[0.05em] rounded-xl px-[0.1em] py-[0.15em] font-tth19-display text-[clamp(54px,12.5vw,128px)] font-bold leading-none tracking-[-0.01em] no-underline focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tth19-acc">
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:0;--tth19-c:oklch(0.62 0.21 25)">W</i><i style="--i:1;--tth19-c:oklch(0.66 0.18 60)">I</i><i style="--i:2;--tth19-c:oklch(0.6 0.17 145)">G</i><i style="--i:3;--tth19-c:oklch(0.58 0.15 230)">G</i><i style="--i:4;--tth19-c:oklch(0.55 0.2 290)">L</i><i style="--i:5;--tth19-c:oklch(0.6 0.22 345)">E</i></span>
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:6;--tth19-c:oklch(0.66 0.18 60)">C</i><i style="--i:7;--tth19-c:oklch(0.6 0.17 145)">L</i><i style="--i:8;--tth19-c:oklch(0.58 0.15 230)">U</i><i style="--i:9;--tth19-c:oklch(0.55 0.2 290)">B</i></span>
</a>
<p class="max-w-[40ch] text-[15.5px] font-semibold leading-[1.6] text-tth19-mut">Blocks in term one, robots by term three. Snacks throughout.</p>
<a href="#tth19-top" class="group/btn mt-1.5 flex min-h-[58px] items-center justify-center gap-2 rounded-full bg-tth19-acc px-[34px] font-tth19-display text-[17px] font-bold tracking-[0.01em] text-white no-underline shadow-[0_6px_0_0_var(--color-tth19-acc-deep)] transition-[scale,box-shadow,translate] duration-450 ease-[cubic-bezier(.28,2.2,.4,1)] hover:scale-x-108 hover:scale-y-92 focus-visible:scale-x-108 focus-visible:scale-y-92 focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tth19-ink active:translate-y-1 active:scale-x-94 active:scale-y-104 active:shadow-[0_2px_0_0_var(--color-tth19-acc-deep)] active:duration-80 motion-reduce:transition-none">Save a spot<span aria-hidden="true" class="transition-transform duration-300 group-hover/btn:translate-x-1">→</span></a>
<p class="max-w-[56ch] text-[12.5px] leading-[1.7] text-tth19-mut">Hover the name (squash & stretch, staggered 40ms) — then press the button and feel it squash. Origin sits at the baseline so letters bounce off the floor.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
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-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
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=Fredoka:wght@400..700&family=Nunito:wght@600;800&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=Fredoka:wght@400..700&family=Nunito:wght@600;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
.word:hover i{animation:…;animation-delay:calc(var(--i)*40ms)}becamegroup-hover/word:*:animate-tth19-rubber+*:[animation-delay:…]on the two parent spans.color:var(--c)became v4's variable shorthandtext-(--tth19-c).- UI upgrade from the CSS original:
ui-roundedonly exists on Apple devices — everyone else got Segoe. Fredoka now gives every platform the same rounded, kid-friendly letterforms. - UI upgrade: per-letter colours were darkened to clear 3:1 on cream (the yellow and green failed), the CTA uses white on a deeper red (4.6:1) and its arrow nudges on hover.
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 Rubber Band Text Hover Animation
Source: https://codefronts.com/motion/tailwind-css-text-hover-effects/tailwind-elastic-rubber-band-text-hover-animation/
A kids' coding-camp wordmark where every letter squashes and stretches like rubber, staggered 40ms and recoloured per letter. The whole animation is group-hover/word:*:animate-tth19-rubber on the parent with *:[animation-delay:calc(var(--i)*40ms)]; the CTA squashes on hover and presses with active:.
## HTML
```html
<section class="tth-19 w-full min-h-svh grid place-items-center bg-tth19-bg bg-[radial-gradient(85%_70%_at_50%_18%,var(--color-tth19-glow),var(--color-tth19-bg)_70%)] px-5 py-14 font-tth19-sans text-tth19-ink" aria-label="Elastic rubber band text demo">
<div class="flex flex-col items-center gap-[clamp(14px,2.8vw,24px)] text-center">
<p class="text-[13px] font-extrabold uppercase tracking-[0.2em] text-tth19-mut">After-school coding camp · ages 7–11</p>
<a id="tth19-top" href="#tth19-top" aria-label="WIGGLE CLUB" class="group/word flex flex-wrap justify-center gap-x-[0.35em] gap-y-[0.05em] rounded-xl px-[0.1em] py-[0.15em] font-tth19-display text-[clamp(54px,12.5vw,128px)] font-bold leading-none tracking-[-0.01em] no-underline focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tth19-acc">
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:0;--tth19-c:oklch(0.62 0.21 25)">W</i><i style="--i:1;--tth19-c:oklch(0.66 0.18 60)">I</i><i style="--i:2;--tth19-c:oklch(0.6 0.17 145)">G</i><i style="--i:3;--tth19-c:oklch(0.58 0.15 230)">G</i><i style="--i:4;--tth19-c:oklch(0.55 0.2 290)">L</i><i style="--i:5;--tth19-c:oklch(0.6 0.22 345)">E</i></span>
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:6;--tth19-c:oklch(0.66 0.18 60)">C</i><i style="--i:7;--tth19-c:oklch(0.6 0.17 145)">L</i><i style="--i:8;--tth19-c:oklch(0.58 0.15 230)">U</i><i style="--i:9;--tth19-c:oklch(0.55 0.2 290)">B</i></span>
</a>
<p class="max-w-[40ch] text-[15.5px] font-semibold leading-[1.6] text-tth19-mut">Blocks in term one, robots by term three. Snacks throughout.</p>
<a href="#tth19-top" class="group/btn mt-1.5 flex min-h-[58px] items-center justify-center gap-2 rounded-full bg-tth19-acc px-[34px] font-tth19-display text-[17px] font-bold tracking-[0.01em] text-white no-underline shadow-[0_6px_0_0_var(--color-tth19-acc-deep)] transition-[scale,box-shadow,translate] duration-450 ease-[cubic-bezier(.28,2.2,.4,1)] hover:scale-x-108 hover:scale-y-92 focus-visible:scale-x-108 focus-visible:scale-y-92 focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tth19-ink active:translate-y-1 active:scale-x-94 active:scale-y-104 active:shadow-[0_2px_0_0_var(--color-tth19-acc-deep)] active:duration-80 motion-reduce:transition-none">Save a spot<span aria-hidden="true" class="transition-transform duration-300 group-hover/btn:translate-x-1">→</span></a>
<p class="max-w-[56ch] text-[12.5px] leading-[1.7] text-tth19-mut">Hover the name (squash & stretch, staggered 40ms) — then press the button and feel it squash. Origin sits at the baseline so letters bounce off the floor.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
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 Rubber Band Text Hover Animation
Source: https://codefronts.com/motion/tailwind-css-text-hover-effects/tailwind-elastic-rubber-band-text-hover-animation/
A kids' coding-camp wordmark where every letter squashes and stretches like rubber, staggered 40ms and recoloured per letter. The whole animation is group-hover/word:*:animate-tth19-rubber on the parent with *:[animation-delay:calc(var(--i)*40ms)]; the CTA squashes on hover and presses with active:.
## HTML
```html
<section class="tth-19 w-full min-h-svh grid place-items-center bg-tth19-bg bg-[radial-gradient(85%_70%_at_50%_18%,var(--color-tth19-glow),var(--color-tth19-bg)_70%)] px-5 py-14 font-tth19-sans text-tth19-ink" aria-label="Elastic rubber band text demo">
<div class="flex flex-col items-center gap-[clamp(14px,2.8vw,24px)] text-center">
<p class="text-[13px] font-extrabold uppercase tracking-[0.2em] text-tth19-mut">After-school coding camp · ages 7–11</p>
<a id="tth19-top" href="#tth19-top" aria-label="WIGGLE CLUB" class="group/word flex flex-wrap justify-center gap-x-[0.35em] gap-y-[0.05em] rounded-xl px-[0.1em] py-[0.15em] font-tth19-display text-[clamp(54px,12.5vw,128px)] font-bold leading-none tracking-[-0.01em] no-underline focus-visible:outline-3 focus-visible:outline-offset-8 focus-visible:outline-tth19-acc">
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:0;--tth19-c:oklch(0.62 0.21 25)">W</i><i style="--i:1;--tth19-c:oklch(0.66 0.18 60)">I</i><i style="--i:2;--tth19-c:oklch(0.6 0.17 145)">G</i><i style="--i:3;--tth19-c:oklch(0.58 0.15 230)">G</i><i style="--i:4;--tth19-c:oklch(0.55 0.2 290)">L</i><i style="--i:5;--tth19-c:oklch(0.6 0.22 345)">E</i></span>
<span aria-hidden="true" class="*:inline-block *:not-italic *:origin-[50%_78%] *:transition-colors *:duration-300 *:[animation-delay:calc(var(--i)*40ms)] group-hover/word:*:animate-tth19-rubber group-hover/word:*:text-(--tth19-c) group-focus-visible/word:*:animate-tth19-rubber group-focus-visible/word:*:text-(--tth19-c) motion-reduce:*:animate-none!"><i style="--i:6;--tth19-c:oklch(0.66 0.18 60)">C</i><i style="--i:7;--tth19-c:oklch(0.6 0.17 145)">L</i><i style="--i:8;--tth19-c:oklch(0.58 0.15 230)">U</i><i style="--i:9;--tth19-c:oklch(0.55 0.2 290)">B</i></span>
</a>
<p class="max-w-[40ch] text-[15.5px] font-semibold leading-[1.6] text-tth19-mut">Blocks in term one, robots by term three. Snacks throughout.</p>
<a href="#tth19-top" class="group/btn mt-1.5 flex min-h-[58px] items-center justify-center gap-2 rounded-full bg-tth19-acc px-[34px] font-tth19-display text-[17px] font-bold tracking-[0.01em] text-white no-underline shadow-[0_6px_0_0_var(--color-tth19-acc-deep)] transition-[scale,box-shadow,translate] duration-450 ease-[cubic-bezier(.28,2.2,.4,1)] hover:scale-x-108 hover:scale-y-92 focus-visible:scale-x-108 focus-visible:scale-y-92 focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tth19-ink active:translate-y-1 active:scale-x-94 active:scale-y-104 active:shadow-[0_2px_0_0_var(--color-tth19-acc-deep)] active:duration-80 motion-reduce:transition-none">Save a spot<span aria-hidden="true" class="transition-transform duration-300 group-hover/btn:translate-x-1">→</span></a>
<p class="max-w-[56ch] text-[12.5px] leading-[1.7] text-tth19-mut">Hover the name (squash & stretch, staggered 40ms) — then press the button and feel it squash. Origin sits at the baseline so letters bounce off the floor.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tth19-bg: #faf6e9;
--color-tth19-bg: oklch(0.965 0.02 95);
--color-tth19-glow: #fdfbf4;
--color-tth19-glow: oklch(0.985 0.012 95);
--color-tth19-ink: #352a57;
--color-tth19-ink: oklch(0.28 0.05 280);
--color-tth19-mut: #5a5373;
--color-tth19-mut: oklch(0.45 0.04 285);
--color-tth19-acc: #e3392f;
--color-tth19-acc: oklch(0.6 0.21 25);
--color-tth19-acc-deep: #8f2019;
--color-tth19-acc-deep: oklch(0.42 0.15 25);
--font-tth19-display: 'Fredoka', ui-rounded, system-ui, sans-serif;
--font-tth19-sans: 'Nunito', system-ui, sans-serif;
--animate-tth19-rubber: tth19-rubber 0.8s cubic-bezier(0.3, 0.6, 0.4, 1) both;
@keyframes tth19-rubber {
0% {
transform: scale(1, 1);
}
30% {
transform: scale(1.28, 0.72);
}
42% {
transform: scale(0.78, 1.24);
}
56% {
transform: scale(1.14, 0.88);
}
70% {
transform: scale(0.94, 1.06);
}
84% {
transform: scale(1.03, 0.98);
}
100% {
transform: scale(1, 1);
}
}
}
.tth-19 {
width: 100%;
min-height: 100svh;
}
```How this works
Squash and stretch is the animator's oldest rule: when something squashes vertically it bulges horizontally, preserving volume. The keyframe alternates scale(1.28,.72) and scale(.78,1.24) with shrinking amplitude, like a damped spring.
<span class="*:inline-block *:origin-[50%_78%]
*:[animation-delay:calc(var(--i)*40ms)]
group-hover/word:*:animate-tth19-rubber
group-hover/word:*:text-(--tth19-c)">
<i style="--i:0;--tth19-c:oklch(0.62 0.21 25)">W</i>…Origin at 78% — just above the baseline — means letters compress into the floor rather than toward their middle, which reads as weight.
v4 shorthand. text-(--tth19-c) is v4's syntax for text-[var(--tth19-c)].
Make it yours
- Wilder: raise the first keyframe to
scale(1.4,.6). - Loop while hovered: add
infiniteto the animation token (and a pause between loops). - Single colour: drop the per-letter
--tth19-cand usegroup-hover/word:*:text-tth19-acc.
Gotchas — read before shipping
- Keyframe animations restart on re-hover (unlike demo 10's transitions) — that's the right feel for a bounce.
bothfill mode keeps the delayed letters at scale 1 before their turn.- Letters must be
inline-blockto transform.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Universal; the floor is Tailwind v4's sheet.