[css] Wavy shape with css

I'm trying to recreate this image with CSS:

wayvy shape

I would not need it to repeat. This is what I started but it just has a straight line:

_x000D_
_x000D_
#wave {_x000D_
  position: absolute;_x000D_
  height: 70px;_x000D_
  width: 600px;_x000D_
  background: #e0efe3;_x000D_
}
_x000D_
<div id="wave"></div>
_x000D_
_x000D_
_x000D_

This question is related to css css-shapes

The answer is


I'm not sure it's your shape but it's close - you can play with the values:

https://jsfiddle.net/7fjSc/9/

_x000D_
_x000D_
#wave {_x000D_
  position: relative;_x000D_
  height: 70px;_x000D_
  width: 600px;_x000D_
  background: #e0efe3;_x000D_
}_x000D_
#wave:before {_x000D_
  content: "";_x000D_
  display: block;_x000D_
  position: absolute;_x000D_
  border-radius: 100% 50%;_x000D_
  width: 340px;_x000D_
  height: 80px;_x000D_
  background-color: white;_x000D_
  right: -5px;_x000D_
  top: 40px;_x000D_
}_x000D_
#wave:after {_x000D_
  content: "";_x000D_
  display: block;_x000D_
  position: absolute;_x000D_
  border-radius: 100% 50%;_x000D_
  width: 300px;_x000D_
  height: 70px;_x000D_
  background-color: #e0efe3;_x000D_
  left: 0;_x000D_
  top: 27px;_x000D_
}
_x000D_
<div id="wave"></div>
_x000D_
_x000D_
_x000D_


My pure CSS implementation based on above with 100% width. Hope it helps!

_x000D_
_x000D_
#wave-container {_x000D_
  width: 100%;_x000D_
  height: 100px;_x000D_
  overflow: hidden;_x000D_
}_x000D_
_x000D_
#wave {_x000D_
  display: block;_x000D_
  position: relative;_x000D_
  height: 40px;_x000D_
  background: black;_x000D_
}_x000D_
_x000D_
#wave:before {_x000D_
  content: "";_x000D_
  display: block;_x000D_
  position: absolute;_x000D_
  border-radius: 100%;_x000D_
  width: 100%;_x000D_
  height: 300px;_x000D_
  background-color: white;_x000D_
  right: -25%;_x000D_
  top: 20px_x000D_
}_x000D_
_x000D_
#wave:after {_x000D_
  content: "";_x000D_
  display: block;_x000D_
  position: absolute;_x000D_
  border-radius: 100%;_x000D_
  width: 100%;_x000D_
  height: 300px;_x000D_
  background-color: black;_x000D_
  left: -25%;_x000D_
  top: -240px;_x000D_
}
_x000D_
<div id="wave-container">_x000D_
  <div id="wave">_x000D_
  </div>_x000D_
</div>
_x000D_
_x000D_
_x000D_


Here's another way to do it :) The concept is to create a clip-path polygon with the wave as one side.

This approach is fairly flexible. You can change the position (left, right, top or bottom) in which the wave appears, change the wave function to any function(t) which maps to [0,1]). The polygon can also be used for shape-outside, which lets text flow around the wave when in 'left' or 'right' orientation.

At the end, an example you can uncomment which demonstrates animating the wave.

_x000D_
_x000D_
 _x000D_
_x000D_
function PolyCalc(f /*a function(t)  from [0, infinity) => [0, 1]*/, _x000D_
                  s, /*a slice function(y, i) from y [0,1] => [0, 1], with slice index, i, in [0, n]*/_x000D_
         w /*window size in seconds*/,_x000D_
                  n /*sample size*/,_x000D_
                  o /*orientation => left/right/top/bottom - the 'flat edge' of the polygon*/ _x000D_
                  ) _x000D_
{_x000D_
 this.polyStart = "polygon(";_x000D_
  this.polyLeft = this.polyStart + "0% 0%, "; //starts in the top left corner_x000D_
  this.polyRight = this.polyStart + "100% 0%, "; //starts in the top right corner_x000D_
  this.polyTop = this.polyStart + "0% 0%, "; // starts in the top left corner_x000D_
  this.polyBottom = this.polyStart + "0% 100%, ";//starts in the bottom left corner_x000D_
  _x000D_
  var self = this;_x000D_
  self.mapFunc = s;_x000D_
  this.func = f;_x000D_
  this.window = w;_x000D_
  this.count = n;_x000D_
  var dt = w/n;  _x000D_
_x000D_
  switch(o) {_x000D_
    case "top":_x000D_
      this.poly = this.polyTop; break;_x000D_
    case "bottom":_x000D_
      this.poly = this.polyBottom; break;_x000D_
   case "right":_x000D_
     this.poly = this.polyRight; break;_x000D_
   case "left":_x000D_
   default:_x000D_
    this.poly = this.polyLeft; break;_x000D_
    }_x000D_
    _x000D_
  this.CalcPolygon = function(t) {_x000D_
   var p = this.poly;_x000D_
    for (i = 0; i < this.count; i++) {_x000D_
      x = 100 * i/(this.count-1.0);_x000D_
      y = this.func(t + i*dt);_x000D_
      if (typeof self.mapFunc !== 'undefined')_x000D_
       y=self.mapFunc(y, i);_x000D_
      y*=100;_x000D_
      switch(o) {_x000D_
        case "top": _x000D_
          p += x + "% " + y + "%, "; break;_x000D_
        case "bottom":_x000D_
          p += x + "% " + (100-y) + "%, "; break;_x000D_
       case "right":_x000D_
         p += (100-y) + "% " + x + "%, "; break;_x000D_
       case "left":_x000D_
        default:_x000D_
         p += y + "% " + x + "%, "; break;          _x000D_
      }_x000D_
    }_x000D_
    _x000D_
    switch(o) { _x000D_
      case "top":_x000D_
        p += "100% 0%)"; break;_x000D_
      case "bottom":_x000D_
        p += "100% 100%)";_x000D_
        break;_x000D_
     case "right":_x000D_
       p += "100% 100%)"; break;_x000D_
     case "left":_x000D_
      default:_x000D_
       p += "0% 100%)"; break;_x000D_
    }_x000D_
    _x000D_
    return p;_x000D_
  }_x000D_
};_x000D_
_x000D_
var text = document.querySelector("#text");_x000D_
var divs = document.querySelectorAll(".wave");_x000D_
var freq=2*Math.PI; //angular frequency in radians/sec_x000D_
var windowWidth = 1; //the time domain window which determines the range from [t, t+windowWidth] that will be evaluated to create the polygon_x000D_
var sampleSize = 60;_x000D_
divs.forEach(function(wave) {_x000D_
  var loc = wave.classList[1];_x000D_
_x000D_
  var polyCalc = new PolyCalc(_x000D_
   function(t) { //The time domain wave function_x000D_
     return (Math.sin(freq * t) + 1)/2; //sine is [-1, -1], so we remap to [0,1]_x000D_
    },_x000D_
    function(y, i) { //slice function, takes the time domain result and the slice index and returns a new value in [0, 1]  _x000D_
      return MapRange(y, 0.0, 1.0, 0.65, 1.0);  //Here we adjust the range of the wave to 'flatten' it out a bit.  We don't use the index in this case, since it is irrelevant_x000D_
    },_x000D_
    windowWidth, //1 second, which with an angular frequency of 2pi rads/sec will produce one full period._x000D_
    sampleSize, //the number of samples to make, the larger the number, the smoother the curve, but the more pionts in the final polygon_x000D_
    loc //the location_x000D_
  );_x000D_
  _x000D_
    var polyText = polyCalc.CalcPolygon(0);_x000D_
    wave.style.clipPath = polyText;_x000D_
    wave.style.shapeOutside = polyText;_x000D_
    wave.addEventListener("click",function(e) {document.querySelector("#polygon").innerText = polyText;});_x000D_
  });_x000D_
_x000D_
function MapRange(value, min, max, newMin, newMax) {_x000D_
  return value * (newMax - newMin)/(max-min) + newMin;_x000D_
}_x000D_
_x000D_
//Animation - animate the wave by uncommenting this section_x000D_
//Also demonstrates a slice function which uses the index of the slice to alter the output for a dampening effect._x000D_
/*_x000D_
var t = 0;_x000D_
var speed = 1/180;_x000D_
_x000D_
var polyTop = document.querySelector(".top");_x000D_
_x000D_
var polyTopCalc = new PolyCalc(_x000D_
   function(t) {_x000D_
     return (Math.sin(freq * t) + 1)/2;_x000D_
    },_x000D_
    function(y, i) {       _x000D_
      return MapRange(y, 0.0, 1.0, (sampleSize-i)/sampleSize, 1.0);_x000D_
    },_x000D_
    windowWidth, sampleSize, "top"_x000D_
  );_x000D_
_x000D_
function animate() {_x000D_
  var polyT = polyTopCalc.CalcPolygon(t);    _x000D_
    t+= speed;_x000D_
    polyTop.style.clipPath = polyT;    _x000D_
    requestAnimationFrame(animate);_x000D_
}_x000D_
_x000D_
requestAnimationFrame(animate);_x000D_
*/
_x000D_
div div {_x000D_
  padding:10px;_x000D_
  /*overflow:scroll;*/_x000D_
}_x000D_
_x000D_
.left {_x000D_
  height:100%;_x000D_
  width:35%;_x000D_
  float:left;_x000D_
}_x000D_
_x000D_
.right {_x000D_
  height:200px;_x000D_
  width:35%;_x000D_
  float:right;_x000D_
}_x000D_
_x000D_
.top { _x000D_
  width:100%;_x000D_
  height: 200px;  _x000D_
}_x000D_
_x000D_
.bottom {_x000D_
  width:100%;_x000D_
  height:200px;_x000D_
}_x000D_
_x000D_
.green {_x000D_
  background:linear-gradient(to bottom, #b4ddb4 0%,#83c783 17%,#52b152 33%,#008a00 67%,#005700 83%,#002400 100%); _x000D_
} _x000D_
_x000D_
.mainContainer {_x000D_
  width:100%;_x000D_
  float:left;_x000D_
}_x000D_
_x000D_
#polygon {_x000D_
  padding-left:20px;_x000D_
  margin-left:20px;_x000D_
  width:100%;_x000D_
}
_x000D_
<div class="mainContainer">_x000D_
_x000D_
  <div class="wave top green">_x000D_
    Click to see the polygon CSS_x000D_
  </div>_x000D_
  _x000D_
  <!--div class="wave left green">_x000D_
  </div-->_x000D_
  <!--div class="wave right green">_x000D_
  </div-->  _x000D_
  <!--div class="wave bottom green"></div-->  _x000D_
</div>_x000D_
<div id="polygon"></div>
_x000D_
_x000D_
_x000D_


I think this is the right way to make a shape like you want. By using the SVG possibilities, and an container to keep the shape responsive.

_x000D_
_x000D_
svg {_x000D_
  display: inline-block;_x000D_
  position: absolute;_x000D_
  top: 0;_x000D_
  left: 0;_x000D_
}_x000D_
.container {_x000D_
  display: inline-block;_x000D_
  position: relative;_x000D_
  width: 100%;_x000D_
  padding-bottom: 100%;_x000D_
  vertical-align: middle;_x000D_
  overflow: hidden;_x000D_
}
_x000D_
<div class="container">_x000D_
  <svg viewBox="0 0 500 500" preserveAspectRatio="xMinYMin meet">_x000D_
    <path d="M0,100 C150,200 350,0 500,100 L500,00 L0,0 Z" style="stroke: none; fill:red;"></path>_x000D_
  </svg>_x000D_
</div>
_x000D_
_x000D_
_x000D_


Recently an awesome tool called Get Waves was introduced where you can simply from UI create your own waves and then export this to SVG format. This is as simple as going to the https://getwaves.io/ website and enjoying it!

Edit:

Recently I've discovered also a new tool - https://shapedivider.app/


I like ThomasA's answer, but wanted a more realistic context with the wave being used to separate two divs. So I created a more complete demo where the separator SVG gets positioned perfectly between the two divs.

css wavy divider in CSS

Now I thought it would be cool to take it further. What if we could do this all in CSS without the need for the inline SVG? The point being to avoid extra markup. Here's how I did it:

Two simple <div>:

_x000D_
_x000D_
/** CSS using pseudo-elements: **/_x000D_
_x000D_
#A {_x000D_
  background: #0074D9;_x000D_
}_x000D_
_x000D_
#B {_x000D_
  background: #7FDBFF;_x000D_
}_x000D_
_x000D_
#A::after {_x000D_
  content: "";_x000D_
  position: relative;_x000D_
  left: -3rem;_x000D_
  /* padding * -1 */_x000D_
  top: calc( 3rem - 4rem / 2);_x000D_
  /* padding - height/2 */_x000D_
  float: left;_x000D_
  display: block;_x000D_
  height: 4rem;_x000D_
  width: 100vw;_x000D_
  background: hsla(0, 0%, 100%, 0.5);_x000D_
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 70 500 60' preserveAspectRatio='none'%3E%3Crect x='0' y='0' width='500' height='500' style='stroke: none; fill: %237FDBFF;' /%3E%3Cpath d='M0,100 C150,200 350,0 500,100 L500,00 L0,0 Z' style='stroke: none; fill: %230074D9;'%3E%3C/path%3E%3C/svg%3E");_x000D_
  background-size: 100% 100%;_x000D_
}_x000D_
_x000D_
_x000D_
/** Cosmetics **/_x000D_
_x000D_
* {_x000D_
  margin: 0;_x000D_
}_x000D_
_x000D_
#A,_x000D_
#B {_x000D_
  padding: 3rem;_x000D_
}_x000D_
_x000D_
div {_x000D_
  font-family: monospace;_x000D_
  font-size: 1.2rem;_x000D_
  line-height: 1.2;_x000D_
}_x000D_
_x000D_
#A {_x000D_
  color: white;_x000D_
}
_x000D_
<div id="A">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nec quam tincidunt, iaculis mi non, hendrerit felis. Nulla pretium lectus et arcu tempus, quis luctus ex imperdiet. In facilisis nulla suscipit ornare finibus. …_x000D_
</div>_x000D_
_x000D_
<div id="B" class="wavy">… In iaculis fermentum lacus vel porttitor. Vestibulum congue elementum neque eget feugiat. Donec suscipit diam ligula, aliquam consequat tellus sagittis porttitor. Sed sodales leo nisl, ut consequat est ornare eleifend. Cras et semper mi, in porta nunc.</div>
_x000D_
_x000D_
_x000D_

Demo Wavy divider (with CSS pseudo-elements to avoid extra markup)

It was a bit trickier to position than with an inline SVG but works just as well. (Could use CSS custom properties or pre-processor variables to keep the height and padding easy to read.)

To edit the colors, you need to edit the URL-encoded SVG itself.

Pay attention (like in the first demo) to a change in the viewBox to get rid of unwanted spaces in the SVG. (Another option would be to draw a different SVG.)

Another thing to pay attention to here is the background-size set to 100% 100% to get it to stretch in both directions.


My implementation uses the svg element in html and I also made a generator for making the wave you want:

https://smooth.ie/blogs/news/svg-wavey-transitions-between-sections

<div style="height: 150px; overflow: hidden;">
  <svg viewBox="0 0 500 150" preserveAspectRatio="none" style="height: 100%; width: 100%;">
    <path d="M0.00,92.27 C216.83,192.92 304.30,8.39 500.00,109.03 L500.00,0.00 L0.00,0.00 Z" style="stroke: none;fill: #e1efe3;"></path>
  </svg>
</div>

https://jsfiddle.net/1b8L7nax/5/