12 / 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 Sine Wave Section Divider Transition
A mathematically correct sine — sampled from y = A·sin(x) rather than eyeballed with bezier handles — animated as a travelling waveform. The signal-processing look that tech, audio and web3 landing pages keep asking for, plus a stroked oscilloscope variant.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="twd-12 w-full min-h-svh flex flex-col font-twd12-sans">
<!-- Travelling sine with interference layer -->
<div class="relative overflow-hidden bg-twd12-void text-white px-6 pt-18 pb-31 text-center">
<p class="font-twd12-mono text-xs tracking-[0.2em] text-twd12-signal mb-3">y = A · sin(x)</p>
<h2 class="text-[clamp(25px,4vw,40px)] font-semibold">Signal, not decoration</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(64px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:18s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd12-run motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-floor text-twd12-floorink px-6 pt-8 pb-15 text-center">
<p class="text-[14px] font-twd12-mono">Each half-period is identical, so the loop is mathematically seamless.</p>
</div>
<!-- Oscilloscope trace -->
<div class="relative overflow-hidden bg-twd12-crt text-twd12-oncrt px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(23px,3.6vw,34px)] font-semibold">Stroked waveform seam</h2>
<div aria-hidden="true" class="absolute inset-x-0 bottom-0 h-[clamp(56px,8vw,100px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:8s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-crtnext text-twd12-oncrt px-6 pt-7 pb-14 text-center border-t border-twd12-trace/35">
<p class="text-[14px] font-twd12-mono">A stroke instead of a fill turns the divider into an instrument readout.</p>
</div>
</section><section class="twd-12 w-full min-h-svh flex flex-col font-twd12-sans">
<!-- Travelling sine with interference layer -->
<div class="relative overflow-hidden bg-twd12-void text-white px-6 pt-18 pb-31 text-center">
<p class="font-twd12-mono text-xs tracking-[0.2em] text-twd12-signal mb-3">y = A · sin(x)</p>
<h2 class="text-[clamp(25px,4vw,40px)] font-semibold">Signal, not decoration</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(64px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:18s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd12-run motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-floor text-twd12-floorink px-6 pt-8 pb-15 text-center">
<p class="text-[14px] font-twd12-mono">Each half-period is identical, so the loop is mathematically seamless.</p>
</div>
<!-- Oscilloscope trace -->
<div class="relative overflow-hidden bg-twd12-crt text-twd12-oncrt px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(23px,3.6vw,34px)] font-semibold">Stroked waveform seam</h2>
<div aria-hidden="true" class="absolute inset-x-0 bottom-0 h-[clamp(56px,8vw,100px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:8s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-crtnext text-twd12-oncrt px-6 pt-7 pb-14 text-center border-t border-twd12-trace/35">
<p class="text-[14px] font-twd12-mono">A stroke instead of a fill turns the divider into an instrument readout.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
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-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
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.
- Both variants share one
@keyframesand one--animate-*token, with per-layer[animation-duration:...]overrides. Four animated tracks, one keyframe definition. stroke-twd12-trace stroke-3 fill-noneon a path is the port at its most ordinary — and it is worth noting thatvector-effectanddrop-shadowsit in the same class list, so the whole trace treatment is one read.drop-shadow-[0_0_6px_var(--color-twd12-trace)]reads the stroke's own token, which the CSS original could only do via a secondcolor-mix()declaration.- UI change from the CSS original: the oscilloscope section had
bottom:0with no overlap and anoverflow:hiddenparent that clipped the trace's glow at the seam. The track now sits flush withoverflow-visibleon each SVG, so the glow is not sliced, and the caption's border reads the trace token at/35rather than a hand-mixed value.
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 Sine Wave Section Divider Transition
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-animated-sine-wave-section-divider-transition/
A mathematically correct sine — sampled from y = A·sin(x) rather than eyeballed with bezier handles — animated as a travelling waveform. The signal-processing look that tech, audio and web3 landing pages keep asking for, plus a stroked oscilloscope variant.
## HTML
```html
<section class="twd-12 w-full min-h-svh flex flex-col font-twd12-sans">
<!-- Travelling sine with interference layer -->
<div class="relative overflow-hidden bg-twd12-void text-white px-6 pt-18 pb-31 text-center">
<p class="font-twd12-mono text-xs tracking-[0.2em] text-twd12-signal mb-3">y = A · sin(x)</p>
<h2 class="text-[clamp(25px,4vw,40px)] font-semibold">Signal, not decoration</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(64px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:18s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd12-run motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-floor text-twd12-floorink px-6 pt-8 pb-15 text-center">
<p class="text-[14px] font-twd12-mono">Each half-period is identical, so the loop is mathematically seamless.</p>
</div>
<!-- Oscilloscope trace -->
<div class="relative overflow-hidden bg-twd12-crt text-twd12-oncrt px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(23px,3.6vw,34px)] font-semibold">Stroked waveform seam</h2>
<div aria-hidden="true" class="absolute inset-x-0 bottom-0 h-[clamp(56px,8vw,100px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:8s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-crtnext text-twd12-oncrt px-6 pt-7 pb-14 text-center border-t border-twd12-trace/35">
<p class="text-[14px] font-twd12-mono">A stroke instead of a fill turns the divider into an instrument readout.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
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 Sine Wave Section Divider Transition
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-animated-sine-wave-section-divider-transition/
A mathematically correct sine — sampled from y = A·sin(x) rather than eyeballed with bezier handles — animated as a travelling waveform. The signal-processing look that tech, audio and web3 landing pages keep asking for, plus a stroked oscilloscope variant.
## HTML
```html
<section class="twd-12 w-full min-h-svh flex flex-col font-twd12-sans">
<!-- Travelling sine with interference layer -->
<div class="relative overflow-hidden bg-twd12-void text-white px-6 pt-18 pb-31 text-center">
<p class="font-twd12-mono text-xs tracking-[0.2em] text-twd12-signal mb-3">y = A · sin(x)</p>
<h2 class="text-[clamp(25px,4vw,40px)] font-semibold">Signal, not decoration</h2>
<div aria-hidden="true" class="absolute inset-x-0 -bottom-px h-[clamp(64px,10vw,120px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:18s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-signal/34" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,70 C90,40 270,40 360,70 C450,100 630,100 720,70 C810,40 990,40 1080,70 C1170,100 1350,100 1440,70 L1440,120 L0,120 Z"></path></svg>
</div>
<div class="absolute inset-0 flex w-[200%] animate-twd12-run motion-reduce:animate-none">
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
<svg class="block w-1/2 h-full fill-twd12-floor" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,80 C60,50 180,50 240,80 C300,110 420,110 480,80 C540,50 660,50 720,80 C780,110 900,110 960,80 C1020,50 1140,50 1200,80 C1260,110 1380,110 1440,80 L1440,120 L0,120 Z"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-floor text-twd12-floorink px-6 pt-8 pb-15 text-center">
<p class="text-[14px] font-twd12-mono">Each half-period is identical, so the loop is mathematically seamless.</p>
</div>
<!-- Oscilloscope trace -->
<div class="relative overflow-hidden bg-twd12-crt text-twd12-oncrt px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(23px,3.6vw,34px)] font-semibold">Stroked waveform seam</h2>
<div aria-hidden="true" class="absolute inset-x-0 bottom-0 h-[clamp(56px,8vw,100px)] pointer-events-none">
<div class="absolute inset-0 flex w-[200%] animate-twd12-run [animation-duration:8s] motion-reduce:animate-none">
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
<svg class="block w-1/2 h-full overflow-visible" viewBox="0 0 1440 100" preserveAspectRatio="none" focusable="false"><path class="fill-none stroke-twd12-trace stroke-3 [vector-effect:non-scaling-stroke] drop-shadow-[0_0_6px_var(--color-twd12-trace)]" d="M0,50 C60,14 180,14 240,50 C300,86 420,86 480,50 C540,14 660,14 720,50 C780,86 900,86 960,50 C1020,14 1140,14 1200,50 C1260,86 1380,86 1440,50"></path></svg>
</div>
</div>
</div>
<div class="bg-twd12-crtnext text-twd12-oncrt px-6 pt-7 pb-14 text-center border-t border-twd12-trace/35">
<p class="text-[14px] font-twd12-mono">A stroke instead of a fill turns the divider into an instrument readout.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd12-void: #10121c;
--color-twd12-void: oklch(0.15 0.03 265);
--color-twd12-signal: #4fd6a8;
--color-twd12-signal: oklch(0.8 0.15 165);
--color-twd12-floor: #eff7f3;
--color-twd12-floor: oklch(0.96 0.02 165);
--color-twd12-floorink: #25514a;
--color-twd12-floorink: oklch(0.4 0.06 170);
--color-twd12-crt: #0b120e;
--color-twd12-crt: oklch(0.13 0.02 150);
--color-twd12-crtnext: #182118;
--color-twd12-crtnext: oklch(0.2 0.03 150);
--color-twd12-oncrt: #e4fbee;
--color-twd12-oncrt: oklch(0.96 0.03 155);
--color-twd12-trace: #7ff08a;
--color-twd12-trace: oklch(0.88 0.17 145);
--font-twd12-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd12-mono: 'JetBrains Mono', ui-monospace, monospace;
--animate-twd12-run: twd12-run 11s linear infinite;
}
@keyframes twd12-run {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(-50%, 0, 0);
}
}
.twd-12 {
width: 100%;
min-height: 100svh;
}
```How this works
A true sine is built by sampling the function at regular intervals and emitting one cubic segment per half-period, with control points at 1/4 and 3/4 of each half. Because every half-period is identical, the path tiles perfectly — exactly what a seamless loop needs.
<!-- one period = 360 units, amplitude 30 -->
M0,60 C45,30 135,30 180,60 C225,90 315,90 360,60 …Two layers at different speeds make it read as a signal. Where they cross, the interference looks like a waveform rather than a repeating decal; a back layer at 0.6× speed and 34% alpha is enough to break the loop's perceptibility.
The stroked variant swaps the fill for a stroke and non-scaling-stroke, which turns the same geometry into an instrument readout.
Make it yours
- Amplitude: change the control Y values in pairs (30/90) or it stops being a sine.
- Frequency: shorten the period (360 → 240) for a denser signal.
- Add a stroked copy on top of the filled one for a trace over a waveform.
- Tint layers with
fill-*/NNsteps for a spectrum feel.
Gotchas — read before shipping
- Bezier-approximated sines drift if the control points are not at 1/4 and 3/4 of each half-period. This is the one place in the collection where eyeballing the numbers shows.
- Keep the period a clean divisor of the viewBox width, or the tiling breaks at the seam.
- One infinite animation per divider. Stacking five is a measurable battery drain on a phone.
motion-reduce:animate-noneis not optional: a scrolling waveform is a strong vestibular trigger.overflow-visibleon the stroked SVG, or the trace's outer half is clipped by the viewBox at the top and bottom of its swing.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Keyframed transforms and vector-effect are universally supported; the floor is Tailwind v4's generated sheet. Reduced motion falls back to a static waveform.