02 / 14
All 14 in 14 Tailwind CSS Wave Section Dividers
- 01Tailwind CSS SVG Wave Section Divider with Zero Dependencies
- 02Tailwind Animated Wave Section Divider for Landing Pages
- 03Tailwind Responsive SVG Wave Divider at the Bottom of a Hero
- 04Tailwind Stacked Multi Color SVG Wave Section Divider
- 05Tailwind Wave Section Divider Footer Transition
- 06Pure Tailwind Clip Path Wave Divider Without SVG
- 07Tailwind Inverted Curve SVG Wave Divider
- 08Tailwind Wave Divider Full Width Responsive Mobile Fix
- 09Tailwind Gradient Wave Section Divider with a Background Mask
- 10Tailwind Asymmetrical Organic SVG Section Wave
- 11Tailwind Dark Mode Wave Divider Colour Match
- 12Tailwind Animated Sine Wave Section Divider Transition
- 13Tailwind Wave Section Divider with an Overlay Background Image
- 14Tailwind Vertical Wave Divider for Side Navigation
Tailwind Animated Wave Section Divider for Landing Pages
Continuous liquid motion for SaaS and agency heroes: two identical seamless wave tiles slide sideways forever, so the crest never appears to end. The port turns the whole animation into one utility — animate-twd02-drift — because in v4 an --animate-* token mints exactly that.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="twd-02 w-full min-h-svh flex flex-col font-twd02-sans">
<!-- Infinite ocean drift -->
<div class="relative overflow-hidden bg-twd02-hero text-white px-6 pt-19 pb-29 text-center">
<p class="font-twd02-mono text-xs tracking-[0.2em] uppercase text-twd02-onhero mb-3">Landing page hero</p>
<h2 class="text-[clamp(26px,4.2vw,42px)] leading-[1.1] font-semibold">Ship faster with fluid sections</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(60px,9vw,110px)] pointer-events-none">
<div class="flex w-[200%] h-full animate-twd02-drift motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-pale text-twd02-deep px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Transform-only loop — 0 layout, 0 paint, 0 CLS.</p>
</div>
<!-- Three-layer parallax swell -->
<div class="relative overflow-hidden bg-twd02-sea text-white px-6 pt-18 pb-31 text-center">
<h2 class="text-[clamp(25px,4vw,38px)] font-semibold">Layered liquid depth</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(66px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:22s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:15s] [animation-direction:reverse] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:9s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-foam text-twd02-seaink px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Different durations per layer = parallax with no scroll listener.</p>
</div>
</section><section class="twd-02 w-full min-h-svh flex flex-col font-twd02-sans">
<!-- Infinite ocean drift -->
<div class="relative overflow-hidden bg-twd02-hero text-white px-6 pt-19 pb-29 text-center">
<p class="font-twd02-mono text-xs tracking-[0.2em] uppercase text-twd02-onhero mb-3">Landing page hero</p>
<h2 class="text-[clamp(26px,4.2vw,42px)] leading-[1.1] font-semibold">Ship faster with fluid sections</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(60px,9vw,110px)] pointer-events-none">
<div class="flex w-[200%] h-full animate-twd02-drift motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-pale text-twd02-deep px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Transform-only loop — 0 layout, 0 paint, 0 CLS.</p>
</div>
<!-- Three-layer parallax swell -->
<div class="relative overflow-hidden bg-twd02-sea text-white px-6 pt-18 pb-31 text-center">
<h2 class="text-[clamp(25px,4vw,38px)] font-semibold">Layered liquid depth</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(66px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:22s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:15s] [animation-direction:reverse] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:9s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-foam text-twd02-seaink px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Different durations per layer = parallax with no scroll listener.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
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-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&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&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The animation becoming a token is the cleanest thing in this port:
--animate-twd02-driftnames the whole shorthand, and per-layer speed is an arbitrary[animation-duration:...]override rather than three near-identical class rules. color-mix(in oklab, var(--foam) 32%, transparent)collapses tofill-twd02-foam/30. Same result, and the layer order is now readable straight down the markup.@keyframesstays in the stylesheet, as it must — there is no utility form, and the@themeentry is what connects the two.- UI change from the CSS original: the parallax hero lost its
linear-gradientbackground in favour of a flat token. Stacked translucent foam layers over a gradient produced visible banding on the mid layer where the gradient was lightest.
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 Animated Wave Section Divider for Landing Pages
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-animated-wave-section-divider-landing-pages/
Continuous liquid motion for SaaS and agency heroes: two identical seamless wave tiles slide sideways forever, so the crest never appears to end. The port turns the whole animation into one utility — animate-twd02-drift — because in v4 an --animate-* token mints exactly that.
## HTML
```html
<section class="twd-02 w-full min-h-svh flex flex-col font-twd02-sans">
<!-- Infinite ocean drift -->
<div class="relative overflow-hidden bg-twd02-hero text-white px-6 pt-19 pb-29 text-center">
<p class="font-twd02-mono text-xs tracking-[0.2em] uppercase text-twd02-onhero mb-3">Landing page hero</p>
<h2 class="text-[clamp(26px,4.2vw,42px)] leading-[1.1] font-semibold">Ship faster with fluid sections</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(60px,9vw,110px)] pointer-events-none">
<div class="flex w-[200%] h-full animate-twd02-drift motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-pale text-twd02-deep px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Transform-only loop — 0 layout, 0 paint, 0 CLS.</p>
</div>
<!-- Three-layer parallax swell -->
<div class="relative overflow-hidden bg-twd02-sea text-white px-6 pt-18 pb-31 text-center">
<h2 class="text-[clamp(25px,4vw,38px)] font-semibold">Layered liquid depth</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(66px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:22s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:15s] [animation-direction:reverse] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:9s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-foam text-twd02-seaink px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Different durations per layer = parallax with no scroll listener.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
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 Animated Wave Section Divider for Landing Pages
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-animated-wave-section-divider-landing-pages/
Continuous liquid motion for SaaS and agency heroes: two identical seamless wave tiles slide sideways forever, so the crest never appears to end. The port turns the whole animation into one utility — animate-twd02-drift — because in v4 an --animate-* token mints exactly that.
## HTML
```html
<section class="twd-02 w-full min-h-svh flex flex-col font-twd02-sans">
<!-- Infinite ocean drift -->
<div class="relative overflow-hidden bg-twd02-hero text-white px-6 pt-19 pb-29 text-center">
<p class="font-twd02-mono text-xs tracking-[0.2em] uppercase text-twd02-onhero mb-3">Landing page hero</p>
<h2 class="text-[clamp(26px,4.2vw,42px)] leading-[1.1] font-semibold">Ship faster with fluid sections</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(60px,9vw,110px)] pointer-events-none">
<div class="flex w-[200%] h-full animate-twd02-drift motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-pale" viewBox="0 0 1440 110" preserveAspectRatio="none" focusable="false"><path d="M0,60 C180,100 360,20 720,60 C1080,100 1260,20 1440,60 L1440,110 L0,110 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-pale text-twd02-deep px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Transform-only loop — 0 layout, 0 paint, 0 CLS.</p>
</div>
<!-- Three-layer parallax swell -->
<div class="relative overflow-hidden bg-twd02-sea text-white px-6 pt-18 pb-31 text-center">
<h2 class="text-[clamp(25px,4vw,38px)] font-semibold">Layered liquid depth</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(66px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:22s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/30" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C240,110 480,30 720,70 C960,110 1200,30 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:15s] [animation-direction:reverse] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam/60" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,84 C200,44 420,120 720,84 C1020,48 1240,120 1440,84 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd02-drift [animation-duration:9s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd02-foam" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,96 C260,120 460,64 720,96 C980,124 1180,64 1440,96 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd02-foam text-twd02-seaink px-6 pt-9 pb-16 text-center">
<p class="text-[15px]">Different durations per layer = parallax with no scroll listener.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd02-hero: #2a3a6b;
--color-twd02-hero: oklch(0.34 0.12 262);
--color-twd02-onhero: #b9c2e2;
--color-twd02-onhero: oklch(0.8 0.05 262);
--color-twd02-pale: #f2f5fb;
--color-twd02-pale: oklch(0.96 0.02 255);
--color-twd02-deep: #37426b;
--color-twd02-deep: oklch(0.38 0.07 262);
--color-twd02-sea: #1a6f8e;
--color-twd02-sea: oklch(0.48 0.12 225);
--color-twd02-foam: #f0f8fb;
--color-twd02-foam: oklch(0.97 0.02 215);
--color-twd02-seaink: #22566a;
--color-twd02-seaink: oklch(0.4 0.07 225);
--font-twd02-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd02-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd02-drift: twd02-drift 14s linear infinite;
}
@keyframes twd02-drift {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-02 {
width: 100%;
min-height: 100svh;
}
```How this works
The loop is geometry, not easing. The path is authored so its left and right edges meet at the same Y; two copies sit in a w-[200%] flex track, and the track translates exactly -50% on an infinite linear keyframe. When it lands, copy two is where copy one started.
@theme { --animate-twd02-drift: twd02-drift 14s linear infinite; }
@keyframes twd02-drift { from { transform: translate3d(0,0,0) } to { transform: translate3d(-50%,0,0) } }Depth is three tracks, not three animations. The parallax variant stacks the same utility at different durations via [animation-duration:22s] and direction-reverse, with each layer’s fill stepped by an opacity modifier. Only transform animates, so all three layers stay on the compositor.
Make it yours
- Speed: override the token per layer with
[animation-duration:22s]— 10s energetic, 22s ambient. - Direction:
[animation-direction:reverse]on one layer is what stops the stack reading as a single decal. - Amplitude: edit the control-point Y values in the path; never scale the SVG, which thickens nothing and smears everything.
- Depth: two layers is restraint, three is a brochure, four is mush.
Gotchas — read before shipping
- The path must be seamless left-to-right or the loop visibly jumps once per cycle.
- Translate exactly
-50%of aw-[200%]track. Any other pair of numbers desyncs. - Do not scatter
will-change-transformacross layers — one composited layer per track is already the cheap version. motion-reduce:animate-noneis mandatory, not polite: a permanently moving divider is a vestibular trigger.- Fill the tracks with
fill-*utilities, not a[&_path]:variant, when the colour differs per layer — the fill belongs to the layer.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Keyframed transforms are universal; the floor is Tailwind v4's sheet. Under prefers-reduced-motion the divider renders as a static wave, which is a complete design, not a degraded one.