The Anatomy of an SVG Wave
An SVG wave divider is typically a single <path> element with four commands:
<svg viewBox="0 0 1440 120" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0,64 C320,0 1120,128 1440,64 L1440,120 L0,120 Z" fill="#7c5cfc" />
</svg>
Breaking it down:
M0,64— move to starting point (left edge, middle height)C320,0 1120,128 1440,64— cubic Bezier curve with two control points that define the wave's shapeL1440,120 L0,120— lines down the right side and across the bottomZ— close the path back to the start, creating a fillable shape
Understanding Cubic Bezier Control Points
The C x1,y1 x2,y2 x,y syntax describes a curve from the current point to (x,y), with the curve's shape controlled by (x1,y1) and (x2,y2):
- The first control point pulls the curve near the start
- The second control point pulls the curve near the end
- Control points above the baseline create peaks; below create troughs
- The further the control point is from the baseline, the more dramatic the curve
For a standard "gentle wave" between two points at the same height, set control point Y values in opposite directions — one high, one low — at equal distances from the baseline.
Making Waves Responsive
The critical attribute is preserveAspectRatio="none". By default, SVG maintains aspect ratio, so a wave will keep its designed proportions even if the container is wider. That's wrong for dividers — you want the wave to stretch horizontally to match the container's width.
<svg
viewBox="0 0 1440 120"
preserveAspectRatio="none"
style="display:block; width:100%; height:auto;">
<path d="M0,64 C320,0 1120,128 1440,64 L1440,120 L0,120 Z" fill="#7c5cfc" />
</svg>
The width:100% stretches the SVG to the container. display:block removes the inline-element whitespace that otherwise appears under the SVG. height:auto lets the SVG scale proportionally to maintain the viewBox ratio.
Flipping the Wave
A wave that curves down at the top of a section curves up at the bottom of the previous one. Either design both versions or flip with CSS:
.wave-flip {
transform: rotate(180deg);
}
/* Or horizontally mirror */
.wave-mirror {
transform: scaleX(-1);
}
Layered Parallax Waves
Two or three waves stacked with different opacities and offsets create depth:
<div class="wave-container">
<svg viewBox="0 0 1440 120" preserveAspectRatio="none" class="wave wave-back">
<path d="M0,80 C400,40 1040,120 1440,60 L1440,120 L0,120 Z"
fill="#7c5cfc" opacity="0.3"/>
</svg>
<svg viewBox="0 0 1440 120" preserveAspectRatio="none" class="wave wave-mid">
<path d="M0,70 C360,20 1080,130 1440,50 L1440,120 L0,120 Z"
fill="#7c5cfc" opacity="0.6"/>
</svg>
<svg viewBox="0 0 1440 120" preserveAspectRatio="none" class="wave wave-front">
<path d="M0,64 C320,0 1120,128 1440,64 L1440,120 L0,120 Z"
fill="#7c5cfc"/>
</svg>
</div>
<style>
.wave-container { position: relative; line-height: 0; }
.wave { position: absolute; inset: 0; width: 100%; }
.wave-back { bottom: 10px; }
.wave-mid { bottom: 5px; }
</style>
Animating a Wave
For a subtle ocean effect, animate the d attribute directly with SMIL or use CSS transform: translateX() to slide a wider-than-container wave horizontally:
.wave-animate svg {
width: 200%; /* oversized */
animation: slide 20s linear infinite;
}
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
Key trick: the wave path must tile seamlessly — the left edge shape must match the right edge shape, so the animation loop is invisible.
Gradient-Filled Waves
Solid colors are fine, but gradients feel more modern:
<svg viewBox="0 0 1440 120" preserveAspectRatio="none">
<defs>
<linearGradient id="waveGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#7c5cfc"/>
<stop offset="100%" stop-color="#ec4899"/>
</linearGradient>
</defs>
<path d="M0,64 C320,0 1120,128 1440,64 L1440,120 L0,120 Z"
fill="url(#waveGradient)"/>
</svg>
Performance Notes
- SVGs inline in HTML save an HTTP request vs external files. Use inline for 1-2 waves per page.
- Raw SVG source is typically 300-800 bytes — negligible weight.
- Avoid image fallbacks (
background-image: url(wave.svg)) — they prevent the CSScurrentColortrick for matching section colors. - Animation performance — animate
transform, notpath d. Path morphing triggers expensive re-rasterization.
Frequently Asked Questions
- Why does my SVG wave leave a white gap under it?
- SVG elements are
inlineby default, which introduces line-height whitespace. Fix withdisplay: blockon the SVG, or setline-height: 0on the container. - How do I make the wave exactly match two section colors?
- Two options: (1) place the SVG between two colored sections, fill it with the color of the section below; the viewer sees the wave shape as a transition. (2) Use a gradient fill that transitions from the top section color to the bottom.
- Can I create wave dividers without SVG?
- Pure CSS wave dividers exist but require
mask-imageand are limited to simple sine-wave patterns. SVG gives you full Bezier control. CSSclip-path: polygon()can approximate zigzags but not smooth waves. - Why do my waves look jagged on certain screens?
- Usually missing
preserveAspectRatio="none"— the wave is stretching non-uniformly. Also check that the container has a concrete width (notmax-content). - How do I generate custom wave shapes?
- For hand-crafted waves, use a tool with visual Bezier editing. For quick options, interactive wave generators let you drag control points and copy the path. Alternatively, use Figma's pen tool, then export as SVG and copy the path's
dattribute.