01 / 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 CSS SVG Wave Section Divider with Zero Dependencies
The baseline everyone actually needs, three ways: an inline SVG path, the same wave as a reusable mask-image utility, and a low-amplitude editorial seam. The port's win is text-twd01-next [&_path]:fill-current — the seam colour now lives on the SVG that renders it, so re-theming one divider needs no new CSS rule.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="twd-01 w-full min-h-svh flex flex-col font-twd01-sans">
<!-- Classic inline SVG wave -->
<div class="relative bg-twd01-ink text-white px-6 pt-18 pb-27 text-center">
<p class="font-twd01-mono text-xs tracking-[0.18em] uppercase text-twd01-mute mb-3.5">Pure Tailwind + inline SVG</p>
<h2 class="text-[clamp(26px,4vw,40px)] leading-[1.1] font-semibold mb-2.5">Break the boxy grid</h2>
<p class="text-[15px] text-twd01-onink">One path, one class list, zero dependencies.</p>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(56px,9vw,110px)] text-twd01-next [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 120" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,64 C240,120 480,8 720,40 C960,72 1200,120 1440,72 L1440,120 L0,120 Z"></path>
</svg>
</div>
<div class="bg-twd01-next text-twd01-ink px-6 pt-11 pb-16 text-center">
<p class="text-[15px] text-twd01-body">Next section starts here — the wave is this block’s own edge.</p>
</div>
<!-- Reusable data-URI mask: one arbitrary property, any two sections -->
<div class="bg-twd01-teal text-white px-6 pt-16 pb-10 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold mb-2">Reusable divider utility</h2>
<p class="text-[15px] text-twd01-onteal">The wave lives in a custom property, so the markup stays a single div.</p>
</div>
<div class="bg-twd01-teal">
<div role="presentation" class="-mt-px h-[clamp(48px,7vw,88px)] bg-twd01-pale [mask:var(--twd01-wave)_no-repeat_center/100%_100%]"></div>
</div>
<div class="bg-twd01-pale text-twd01-teal px-6 pt-7 pb-15 text-center">
<p class="text-[15px]">Drop the masked div between any two blocks — <code class="font-twd01-mono text-[13.5px] bg-twd01-tint px-1.5 py-0.5 rounded">[mask:var(--twd01-wave)…]</code></p>
</div>
<!-- Soft double-curve seam -->
<div class="relative bg-twd01-warm text-twd01-deep px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold">Gentle, editorial seam</h2>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(44px,6vw,80px)] text-twd01-deep [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 90" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,52 C180,4 360,4 540,44 C740,88 900,88 1080,50 C1220,20 1330,20 1440,42 L1440,90 L0,90 Z"></path>
</svg>
</div>
<div class="bg-twd01-deep text-twd01-warm px-6 pt-8 pb-16 text-center">
<p class="text-[15px]">Low-amplitude curves read as premium; tall waves read as playful.</p>
</div>
</section><section class="twd-01 w-full min-h-svh flex flex-col font-twd01-sans">
<!-- Classic inline SVG wave -->
<div class="relative bg-twd01-ink text-white px-6 pt-18 pb-27 text-center">
<p class="font-twd01-mono text-xs tracking-[0.18em] uppercase text-twd01-mute mb-3.5">Pure Tailwind + inline SVG</p>
<h2 class="text-[clamp(26px,4vw,40px)] leading-[1.1] font-semibold mb-2.5">Break the boxy grid</h2>
<p class="text-[15px] text-twd01-onink">One path, one class list, zero dependencies.</p>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(56px,9vw,110px)] text-twd01-next [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 120" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,64 C240,120 480,8 720,40 C960,72 1200,120 1440,72 L1440,120 L0,120 Z"></path>
</svg>
</div>
<div class="bg-twd01-next text-twd01-ink px-6 pt-11 pb-16 text-center">
<p class="text-[15px] text-twd01-body">Next section starts here — the wave is this block’s own edge.</p>
</div>
<!-- Reusable data-URI mask: one arbitrary property, any two sections -->
<div class="bg-twd01-teal text-white px-6 pt-16 pb-10 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold mb-2">Reusable divider utility</h2>
<p class="text-[15px] text-twd01-onteal">The wave lives in a custom property, so the markup stays a single div.</p>
</div>
<div class="bg-twd01-teal">
<div role="presentation" class="-mt-px h-[clamp(48px,7vw,88px)] bg-twd01-pale [mask:var(--twd01-wave)_no-repeat_center/100%_100%]"></div>
</div>
<div class="bg-twd01-pale text-twd01-teal px-6 pt-7 pb-15 text-center">
<p class="text-[15px]">Drop the masked div between any two blocks — <code class="font-twd01-mono text-[13.5px] bg-twd01-tint px-1.5 py-0.5 rounded">[mask:var(--twd01-wave)…]</code></p>
</div>
<!-- Soft double-curve seam -->
<div class="relative bg-twd01-warm text-twd01-deep px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold">Gentle, editorial seam</h2>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(44px,6vw,80px)] text-twd01-deep [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 90" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,52 C180,4 360,4 540,44 C740,88 900,88 1080,50 C1220,20 1330,20 1440,42 L1440,90 L0,90 Z"></path>
</svg>
</div>
<div class="bg-twd01-deep text-twd01-warm px-6 pt-8 pb-16 text-center">
<p class="text-[15px]">Low-amplitude curves read as premium; tall waves read as playful.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}@import "tailwindcss";
@theme {
--color-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}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-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}@import "tailwindcss";
@theme {
--color-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}<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.
.wave path{fill:currentColor}becomes[&_path]:fill-currenton the SVG itself. The arbitrary descendant variant looks heavier than the CSS rule for one divider and is dramatically lighter for fourteen — there is no stylesheet to open.- The
clamp()height is the clearest win in the collection: the CSS original needed a declaration in a stylesheet, the port needsh-[clamp(56px,9vw,110px)]next to the geometry it governs. - The mask data URI stays in CSS with its spaces percent-encoded, so the arbitrary property stays legible; the underscore-escaping alternative is unreadable at this length.
- UI change from the CSS original: the masked divider now sits inside a parent carrying the previous section's colour. A mask reveals whatever is behind it, and the original assumed a white page — on any tinted background the area above the curve showed white instead of the section it flows from.
- UI change from the CSS original: body copy was
opacity:.78over a dark surface, which drops effective contrast below 4.5:1 at 15px. It is now a full-opacity token (text-twd01-onink,--color-twd01-body) at the same apparent lightness.
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 CSS SVG Wave Section Divider with Zero Dependencies
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-css-svg-wave-section-divider-zero-dependencies/
The baseline everyone actually needs, three ways: an inline SVG path, the same wave as a reusable mask-image utility, and a low-amplitude editorial seam. The port's win is text-twd01-next [&_path]:fill-current — the seam colour now lives on the SVG that renders it, so re-theming one divider needs no new CSS rule.
## HTML
```html
<section class="twd-01 w-full min-h-svh flex flex-col font-twd01-sans">
<!-- Classic inline SVG wave -->
<div class="relative bg-twd01-ink text-white px-6 pt-18 pb-27 text-center">
<p class="font-twd01-mono text-xs tracking-[0.18em] uppercase text-twd01-mute mb-3.5">Pure Tailwind + inline SVG</p>
<h2 class="text-[clamp(26px,4vw,40px)] leading-[1.1] font-semibold mb-2.5">Break the boxy grid</h2>
<p class="text-[15px] text-twd01-onink">One path, one class list, zero dependencies.</p>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(56px,9vw,110px)] text-twd01-next [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 120" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,64 C240,120 480,8 720,40 C960,72 1200,120 1440,72 L1440,120 L0,120 Z"></path>
</svg>
</div>
<div class="bg-twd01-next text-twd01-ink px-6 pt-11 pb-16 text-center">
<p class="text-[15px] text-twd01-body">Next section starts here — the wave is this block’s own edge.</p>
</div>
<!-- Reusable data-URI mask: one arbitrary property, any two sections -->
<div class="bg-twd01-teal text-white px-6 pt-16 pb-10 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold mb-2">Reusable divider utility</h2>
<p class="text-[15px] text-twd01-onteal">The wave lives in a custom property, so the markup stays a single div.</p>
</div>
<div class="bg-twd01-teal">
<div role="presentation" class="-mt-px h-[clamp(48px,7vw,88px)] bg-twd01-pale [mask:var(--twd01-wave)_no-repeat_center/100%_100%]"></div>
</div>
<div class="bg-twd01-pale text-twd01-teal px-6 pt-7 pb-15 text-center">
<p class="text-[15px]">Drop the masked div between any two blocks — <code class="font-twd01-mono text-[13.5px] bg-twd01-tint px-1.5 py-0.5 rounded">[mask:var(--twd01-wave)…]</code></p>
</div>
<!-- Soft double-curve seam -->
<div class="relative bg-twd01-warm text-twd01-deep px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold">Gentle, editorial seam</h2>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(44px,6vw,80px)] text-twd01-deep [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 90" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,52 C180,4 360,4 540,44 C740,88 900,88 1080,50 C1220,20 1330,20 1440,42 L1440,90 L0,90 Z"></path>
</svg>
</div>
<div class="bg-twd01-deep text-twd01-warm px-6 pt-8 pb-16 text-center">
<p class="text-[15px]">Low-amplitude curves read as premium; tall waves read as playful.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}
```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 CSS SVG Wave Section Divider with Zero Dependencies
Source: https://codefronts.com/layouts/tailwind-css-wave-section-dividers/tailwind-css-svg-wave-section-divider-zero-dependencies/
The baseline everyone actually needs, three ways: an inline SVG path, the same wave as a reusable mask-image utility, and a low-amplitude editorial seam. The port's win is text-twd01-next [&_path]:fill-current — the seam colour now lives on the SVG that renders it, so re-theming one divider needs no new CSS rule.
## HTML
```html
<section class="twd-01 w-full min-h-svh flex flex-col font-twd01-sans">
<!-- Classic inline SVG wave -->
<div class="relative bg-twd01-ink text-white px-6 pt-18 pb-27 text-center">
<p class="font-twd01-mono text-xs tracking-[0.18em] uppercase text-twd01-mute mb-3.5">Pure Tailwind + inline SVG</p>
<h2 class="text-[clamp(26px,4vw,40px)] leading-[1.1] font-semibold mb-2.5">Break the boxy grid</h2>
<p class="text-[15px] text-twd01-onink">One path, one class list, zero dependencies.</p>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(56px,9vw,110px)] text-twd01-next [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 120" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,64 C240,120 480,8 720,40 C960,72 1200,120 1440,72 L1440,120 L0,120 Z"></path>
</svg>
</div>
<div class="bg-twd01-next text-twd01-ink px-6 pt-11 pb-16 text-center">
<p class="text-[15px] text-twd01-body">Next section starts here — the wave is this block’s own edge.</p>
</div>
<!-- Reusable data-URI mask: one arbitrary property, any two sections -->
<div class="bg-twd01-teal text-white px-6 pt-16 pb-10 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold mb-2">Reusable divider utility</h2>
<p class="text-[15px] text-twd01-onteal">The wave lives in a custom property, so the markup stays a single div.</p>
</div>
<div class="bg-twd01-teal">
<div role="presentation" class="-mt-px h-[clamp(48px,7vw,88px)] bg-twd01-pale [mask:var(--twd01-wave)_no-repeat_center/100%_100%]"></div>
</div>
<div class="bg-twd01-pale text-twd01-teal px-6 pt-7 pb-15 text-center">
<p class="text-[15px]">Drop the masked div between any two blocks — <code class="font-twd01-mono text-[13.5px] bg-twd01-tint px-1.5 py-0.5 rounded">[mask:var(--twd01-wave)…]</code></p>
</div>
<!-- Soft double-curve seam -->
<div class="relative bg-twd01-warm text-twd01-deep px-6 pt-17 pb-24 text-center">
<h2 class="text-[clamp(24px,3.6vw,34px)] font-semibold">Gentle, editorial seam</h2>
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(44px,6vw,80px)] text-twd01-deep [&_path]:fill-current pointer-events-none" viewBox="0 0 1440 90" preserveAspectRatio="none" aria-hidden="true" focusable="false">
<path d="M0,52 C180,4 360,4 540,44 C740,88 900,88 1080,50 C1220,20 1330,20 1440,42 L1440,90 L0,90 Z"></path>
</svg>
</div>
<div class="bg-twd01-deep text-twd01-warm px-6 pt-8 pb-16 text-center">
<p class="text-[15px]">Low-amplitude curves read as premium; tall waves read as playful.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-twd01-ink: #2d3555;
--color-twd01-ink: oklch(0.28 0.06 264);
--color-twd01-onink: #d9dcea;
--color-twd01-onink: oklch(0.88 0.02 264);
--color-twd01-mute: #a8aec8;
--color-twd01-mute: oklch(0.74 0.03 264);
--color-twd01-next: #f5f6fa;
--color-twd01-next: oklch(0.97 0.01 264);
--color-twd01-body: #4a5170;
--color-twd01-body: oklch(0.42 0.04 264);
--color-twd01-teal: #1c7f9e;
--color-twd01-teal: oklch(0.55 0.16 205);
--color-twd01-onteal: #dff1f7;
--color-twd01-onteal: oklch(0.93 0.02 205);
--color-twd01-pale: #f7fbfc;
--color-twd01-pale: oklch(0.98 0.01 205);
--color-twd01-tint: #d7eaf1;
--color-twd01-tint: oklch(0.92 0.03 205);
--color-twd01-warm: #f4e6c8;
--color-twd01-warm: oklch(0.93 0.05 78);
--color-twd01-deep: #6b3a2c;
--color-twd01-deep: oklch(0.35 0.07 30);
--font-twd01-sans: 'Space Grotesk', system-ui, sans-serif;
--font-twd01-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.twd-01 {
width: 100%;
min-height: 100svh;
}
/* The wave shape for the mask variant: geometry, not a design-scale value, so it stays
a plain custom property instead of an @theme entry. */
.twd-01 {
--twd01-wave: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20100'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0,40%20C300,110%20640,-10%20960,34%20C1200,68%201320,74%201440,44%20L1440,100%20L0,100%20Z'%20fill='%23000'/%3E%3C/svg%3E");
}
/* Safari < 15.4 mask shim — no variant form exists for the -webkit- prefix. */
.twd-01 [role="presentation"] {
-webkit-mask: var(--twd01-wave) no-repeat center/100% 100%;
}
```How this works
The wave is the next section's own edge. The SVG is absolutely positioned on the seam and filled with the colour of the section it flows into; preserveAspectRatio="none" plus w-full stretches it fluidly instead of cropping.
<svg class="absolute inset-x-0 -bottom-px block w-full h-[clamp(56px,9vw,110px)]
text-twd01-next [&_path]:fill-current pointer-events-none"
viewBox="0 0 1440 120" preserveAspectRatio="none" aria-hidden="true" focusable="false">The mask variant is the reusable one. The path moves into a data-URI custom property in the stylesheet and the divider becomes a <div> wearing one arbitrary property — no SVG node in the markup at all, and the shape is swappable per breakpoint or per theme by reassigning one variable.
Amplitude is art direction. Variant c keeps the same machinery at half the height: low-amplitude curves read premium, tall ones read playful. Nothing about the code changes — only the clamp() and the control points.
Make it yours
- Height: edit the three numbers in
h-[clamp(...)]; 48–160px covers most layouts. - Flip the wave with
-scale-y-100, mirror it with-scale-x-100— both are single utilities in v4. - Retheme through
--color-twd01-next: the SVG reads it viatext-*and the path viafill-current. - Swap the masked shape by reassigning
--twd01-waveinside a media query or adata-themeblock.
Gotchas — read before shipping
- Keep
blockon the SVG. Without it the inline baseline leaves a 4–5px strip of the wrong colour under the curve — the single most reported wave bug. preserveAspectRatio="none"is not optional: the default preserves the ratio and crops the crest on wide screens.- Close the path with
L…Zdown to the viewBox floor or the fill leaks and a sliver of the previous section shows through. pointer-events-noneon every wave. A 110px curve sitting over a CTA will eat the click otherwise.- Tailwind's
mask-*utilities cover the image and position separately; a single-shorthand[mask:...]arbitrary property is clearer for a full mask composition — but ship the-webkit-maskshim in CSS for Safari.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Inline SVG with a viewBox is universally supported; the quoted floor is Tailwind v4's own generated sheet. The mask variant needs the -webkit-mask shim below Safari 15.4, where it otherwise renders as a straight-edged band.