14 CSS Wave Section Dividers04 / 14

Pure CSSMIT licensed

Stacked multi color SVG wave section divider CSS

Two to three offset waves in tinted layers, so the transition between background colours reads as depth rather than a line. The staple move for SaaS pricing pages and agency sites that need a rich section change without an image.

Published

Live Demo
Try it

The code

<div class="wsd-04-group">
<section class="wsd-04a">
  <div class="wsd-04a__top">
    <h2 class="wsd-04a__title">Layered depth, one hue</h2>
    <div class="wsd-04a__stack" aria-hidden="true">
      <svg class="wsd-04a__layer wsd-04a__layer--1" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,70 C260,20 520,120 780,74 C1040,28 1240,60 1440,40 L1440,130 L0,130 Z"></path></svg>
      <svg class="wsd-04a__layer wsd-04a__layer--2" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,86 C240,44 500,126 760,88 C1020,50 1250,96 1440,66 L1440,130 L0,130 Z"></path></svg>
      <svg class="wsd-04a__layer wsd-04a__layer--3" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,100 C220,70 480,132 740,102 C1000,72 1250,120 1440,92 L1440,130 L0,130 Z"></path></svg>
    </div>
  </div>
  <div class="wsd-04a__bottom"><p class="wsd-04a__copy">Back layers are transparent tints; only the front layer is opaque.</p></div>
</section>
<section class="wsd-04b">
  <div class="wsd-04b__top">
    <h2 class="wsd-04b__title">Two-colour ribbon seam</h2>
    <div class="wsd-04b__stack" aria-hidden="true">
      <svg class="wsd-04b__layer wsd-04b__layer--accent" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,54 C300,110 620,6 940,52 C1160,84 1300,92 1440,62 L1440,120 L0,120 Z"></path></svg>
      <svg class="wsd-04b__layer wsd-04b__layer--solid" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,74 C300,126 620,22 940,70 C1160,102 1300,110 1440,80 L1440,120 L0,120 Z"></path></svg>
    </div>
  </div>
  <div class="wsd-04b__bottom"><p class="wsd-04b__copy">An accent ribbon peeks out from under the opaque edge — brand colour, no extra markup weight.</p></div>
</section>
</div>
.wsd-04-group {
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.wsd-04-group > section {
  width: 100%;
}

.wsd-04a,
.wsd-04a *,
.wsd-04a *::before,
.wsd-04a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.wsd-04a {
  --from: oklch(0.34 0.12 285);
  --to: oklch(0.95 0.03 285);
  font-family: 'Segoe UI',system-ui,sans-serif;
}

.wsd-04a__top {
  position: relative;
  overflow: hidden;
  background: var(--from);
  color: #fff;
  padding: 70px 24px 128px;
  text-align: center;
}

.wsd-04a__title {
  font-size: clamp(25px,4vw,38px);
}

.wsd-04a__stack {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: clamp(70px,11vw,130px);
  pointer-events: none;
}

.wsd-04a__layer {
  position: absolute;
  left: 0;
  bottom: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.wsd-04a__layer--1 path {
  fill: color-mix(in oklab,var(--to) 30%,transparent);
}

.wsd-04a__layer--2 path {
  fill: color-mix(in oklab,var(--to) 62%,transparent);
}

.wsd-04a__layer--3 path {
  fill: var(--to);
}

.wsd-04a__bottom {
  background: var(--to);
  color: oklch(0.32 0.08 285);
  padding: 32px 24px 62px;
  text-align: center;
}

.wsd-04a__copy {
  font-size: 15px;
  opacity: .75;
}

.wsd-04b,
.wsd-04b *,
.wsd-04b *::before,
.wsd-04b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.wsd-04b {
  --from: oklch(0.24 0.04 200);
  --to: oklch(0.97 0.01 200);
  --accent: oklch(0.75 0.17 88);
  font-family: 'Segoe UI',system-ui,sans-serif;
}

.wsd-04b__top {
  position: relative;
  overflow: hidden;
  background: var(--from);
  color: #fff;
  padding: 68px 24px 116px;
  text-align: center;
}

.wsd-04b__title {
  font-size: clamp(25px,4vw,38px);
}

.wsd-04b__stack {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: clamp(64px,10vw,120px);
  pointer-events: none;
}

.wsd-04b__layer {
  position: absolute;
  left: 0;
  bottom: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.wsd-04b__layer--accent path {
  fill: var(--accent);
}

.wsd-04b__layer--solid path {
  fill: var(--to);
}

.wsd-04b__bottom {
  background: var(--to);
  color: oklch(0.28 0.04 200);
  padding: 30px 24px 60px;
  text-align: center;
}

.wsd-04b__copy {
  font-size: 15px;
  opacity: .75;
  max-width: 52ch;
  margin: 0 auto;
}
/* No JavaScript — three absolutely positioned SVG layers filled with color-mix() steps of one destination hue; only the last is opaque. */

/* No JavaScript — an accent-filled wave sits 20px above an identical opaque wave, so only a sliver of brand colour shows. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Wave Section Divider 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: Stacked multi color SVG wave section divider CSS
Source: https://codefronts.com/layouts/css-wave-section-dividers/stacked-multi-color-svg-wave-section-divider-css/

Two to three offset waves in tinted layers, so the transition between background colours reads as depth rather than a line. The staple move for SaaS pricing pages and agency sites that need a rich section change without an image.
## HTML
```html
<div class="wsd-04-group">
<section class="wsd-04a">
  <div class="wsd-04a__top">
    <h2 class="wsd-04a__title">Layered depth, one hue</h2>
    <div class="wsd-04a__stack" aria-hidden="true">
      <svg class="wsd-04a__layer wsd-04a__layer--1" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,70 C260,20 520,120 780,74 C1040,28 1240,60 1440,40 L1440,130 L0,130 Z"></path></svg>
      <svg class="wsd-04a__layer wsd-04a__layer--2" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,86 C240,44 500,126 760,88 C1020,50 1250,96 1440,66 L1440,130 L0,130 Z"></path></svg>
      <svg class="wsd-04a__layer wsd-04a__layer--3" viewBox="0 0 1440 130" preserveAspectRatio="none" focusable="false"><path d="M0,100 C220,70 480,132 740,102 C1000,72 1250,120 1440,92 L1440,130 L0,130 Z"></path></svg>
    </div>
  </div>
  <div class="wsd-04a__bottom"><p class="wsd-04a__copy">Back layers are transparent tints; only the front layer is opaque.</p></div>
</section>
<section class="wsd-04b">
  <div class="wsd-04b__top">
    <h2 class="wsd-04b__title">Two-colour ribbon seam</h2>
    <div class="wsd-04b__stack" aria-hidden="true">
      <svg class="wsd-04b__layer wsd-04b__layer--accent" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,54 C300,110 620,6 940,52 C1160,84 1300,92 1440,62 L1440,120 L0,120 Z"></path></svg>
      <svg class="wsd-04b__layer wsd-04b__layer--solid" viewBox="0 0 1440 120" preserveAspectRatio="none" focusable="false"><path d="M0,74 C300,126 620,22 940,70 C1160,102 1300,110 1440,80 L1440,120 L0,120 Z"></path></svg>
    </div>
  </div>
  <div class="wsd-04b__bottom"><p class="wsd-04b__copy">An accent ribbon peeks out from under the opaque edge — brand colour, no extra markup weight.</p></div>
</section>
</div>
```
## CSS
```css
.wsd-04-group {
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.wsd-04-group > section {
  width: 100%;
}

.wsd-04a,
.wsd-04a *,
.wsd-04a *::before,
.wsd-04a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.wsd-04a {
  --from: oklch(0.34 0.12 285);
  --to: oklch(0.95 0.03 285);
  font-family: 'Segoe UI',system-ui,sans-serif;
}

.wsd-04a__top {
  position: relative;
  overflow: hidden;
  background: var(--from);
  color: #fff;
  padding: 70px 24px 128px;
  text-align: center;
}

.wsd-04a__title {
  font-size: clamp(25px,4vw,38px);
}

.wsd-04a__stack {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: clamp(70px,11vw,130px);
  pointer-events: none;
}

.wsd-04a__layer {
  position: absolute;
  left: 0;
  bottom: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.wsd-04a__layer--1 path {
  fill: color-mix(in oklab,var(--to) 30%,transparent);
}

.wsd-04a__layer--2 path {
  fill: color-mix(in oklab,var(--to) 62%,transparent);
}

.wsd-04a__layer--3 path {
  fill: var(--to);
}

.wsd-04a__bottom {
  background: var(--to);
  color: oklch(0.32 0.08 285);
  padding: 32px 24px 62px;
  text-align: center;
}

.wsd-04a__copy {
  font-size: 15px;
  opacity: .75;
}

.wsd-04b,
.wsd-04b *,
.wsd-04b *::before,
.wsd-04b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.wsd-04b {
  --from: oklch(0.24 0.04 200);
  --to: oklch(0.97 0.01 200);
  --accent: oklch(0.75 0.17 88);
  font-family: 'Segoe UI',system-ui,sans-serif;
}

.wsd-04b__top {
  position: relative;
  overflow: hidden;
  background: var(--from);
  color: #fff;
  padding: 68px 24px 116px;
  text-align: center;
}

.wsd-04b__title {
  font-size: clamp(25px,4vw,38px);
}

.wsd-04b__stack {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: clamp(64px,10vw,120px);
  pointer-events: none;
}

.wsd-04b__layer {
  position: absolute;
  left: 0;
  bottom: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.wsd-04b__layer--accent path {
  fill: var(--accent);
}

.wsd-04b__layer--solid path {
  fill: var(--to);
}

.wsd-04b__bottom {
  background: var(--to);
  color: oklch(0.28 0.04 200);
  padding: 30px 24px 60px;
  text-align: center;
}

.wsd-04b__copy {
  font-size: 15px;
  opacity: .75;
  max-width: 52ch;
  margin: 0 auto;
}
```

## JavaScript
```js
/* No JavaScript — three absolutely positioned SVG layers filled with color-mix() steps of one destination hue; only the last is opaque. */

/* No JavaScript — an accent-filled wave sits 20px above an identical opaque wave, so only a sliver of brand colour shows. */
```

How this works

Each layer is its own absolutely positioned SVG in the same stacking container, offset vertically a few pixels and filled with a different color-mix() step of the destination colour. Because the back layers are semi-transparent tints of the same hue, the stack stays harmonious instead of looking like three unrelated ribbons.

.layer:nth-child(1) path{fill:color-mix(in oklab,var(--to) 35%,transparent)}
.layer:nth-child(2) path{fill:color-mix(in oklab,var(--to) 65%,transparent)}
.layer:nth-child(3) path{fill:var(--to)}   /* opaque, on top */

Only the topmost layer is opaque — it is the one that actually joins the next section, so the seam is always solid no matter how the tints resolve.

Make it yours

  • 2 layers for restraint, 3 for a brochure feel; past 4 it turns to mush.
  • Shift each layer's bottom by 6–14px to control how much of it peeks out.
  • Use different paths per layer (not the same path offset) for a less mechanical look.
  • Drive all tints from one --to token so a rebrand is one line.

Gotchas — read before shipping

  • The top layer must be fully opaque or you will see the previous section through the seam.
  • Give layers different z-index/DOM order intentionally — the opaque one last.
  • Tint with color-mix on one hue; mixing unrelated hues creates muddy overlaps.
  • Keep all layers pointer-events:none.

Browser support

ChromeSafariFirefoxEdge
111+16.2+113+111+

color-mix() drives the tints; for older browsers list rgba() fallbacks before the color-mix declarations — the layering itself works everywhere.

Techniques used in this demo

Search CodeFronts

Loading…