Wavy shape with CSS
Asked Answered
M

8

115

I'm trying to recreate this image with CSS:

wavy shape

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

#wave {
  position: absolute;
  height: 70px;
  width: 600px;
  background: #e0efe3;
}
<div id="wave"></div>
Metopic answered 19/6, 2013 at 22:38 Comment(6)
if u want to re create this with css just for size use SVG insteadDesolation
Did you have any thoughts? Anything that you tried?Newbold
Why not just use a background image? At times it is best to not abuse or "leverage" the power of CSS when a simple .png image would cost you maybe 20 bytes.Stroh
For double curved shapes, you can check this question : Double curved shapeMccaffrey
I found this bootsnipp.com/snippets/yN3ZoAnglice
similar question: https://mcmap.net/q/189562/-how-to-create-a-curve-between-two-gradient-using-css/8620333Laburnum
D
97

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

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

#wave {
  position: relative;
  height: 70px;
  width: 600px;
  background: #e0efe3;
}
#wave:before {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100% 50%;
  width: 340px;
  height: 80px;
  background-color: white;
  right: -5px;
  top: 40px;
}
#wave:after {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100% 50%;
  width: 300px;
  height: 70px;
  background-color: #e0efe3;
  left: 0;
  top: 27px;
}
<div id="wave"></div>
Desolation answered 19/6, 2013 at 22:59 Comment(2)
This falls apart without a set width. I always need width=100%. Nice job though.Confine
There's an unsighty gap where both pseudo elements meet.Oar
I
119

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

svg {
  display: inline-block;
  position: absolute;
  top: 0;
  left: 0;
}
.container {
  display: inline-block;
  position: relative;
  width: 100%;
  padding-bottom: 100%;
  vertical-align: middle;
  overflow: hidden;
}
<div class="container">
  <svg viewBox="0 0 500 500" preserveAspectRatio="xMinYMin meet">
    <path d="M0,100 C150,200 350,0 500,100 L500,00 L0,0 Z" style="stroke: none; fill:red;"></path>
  </svg>
</div>
Iliac answered 17/2, 2014 at 9:40 Comment(2)
Although its possible for you to draw and animate shapes with SVG but most people export their SVG files from Vector based software like Corel or Illustrator and Insert in the HTML file, then you can style more with CSS or even manipulate the Nodes with JS or SVG Libraries. and Thats the Case for SVG because It is supposed to solve the difficulties with drawing in CSSDesolation
I built up on this in my answer by adding two divs to make the example more realistic, cropping the SVG to get rid of unwanted margins, and removing the inline SVG in favor of a CSS solution.Oar
D
97

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

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

#wave {
  position: relative;
  height: 70px;
  width: 600px;
  background: #e0efe3;
}
#wave:before {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100% 50%;
  width: 340px;
  height: 80px;
  background-color: white;
  right: -5px;
  top: 40px;
}
#wave:after {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100% 50%;
  width: 300px;
  height: 70px;
  background-color: #e0efe3;
  left: 0;
  top: 27px;
}
<div id="wave"></div>
Desolation answered 19/6, 2013 at 22:59 Comment(2)
This falls apart without a set width. I always need width=100%. Nice job though.Confine
There's an unsighty gap where both pseudo elements meet.Oar
P
65

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/

Premer answered 29/7, 2018 at 20:8 Comment(1)
archive link to the generator: web.archive.org/web/20180810082435/https://smooth.ie/blogs/news/…Viaticum
O
31

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>:

/** CSS using pseudo-elements: **/

#A {
  background: #0074D9;
}

#B {
  background: #7FDBFF;
}

#A::after {
  content: "";
  position: relative;
  left: -3rem;
  /* padding * -1 */
  top: calc( 3rem - 4rem / 2);
  /* padding - height/2 */
  float: left;
  display: block;
  height: 4rem;
  width: 100vw;
  background: hsla(0, 0%, 100%, 0.5);
  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");
  background-size: 100% 100%;
}


/** Cosmetics **/

* {
  margin: 0;
}

#A,
#B {
  padding: 3rem;
}

div {
  font-family: monospace;
  font-size: 1.2rem;
  line-height: 1.2;
}

#A {
  color: white;
}
<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. …
</div>

<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>

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.

Oar answered 6/5, 2019 at 22:4 Comment(0)
D
28

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

#wave-container {
  width: 100%;
  height: 100px;
  overflow: hidden;
}

#wave {
  display: block;
  position: relative;
  height: 40px;
  background: black;
}

#wave:before {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100%;
  width: 100%;
  height: 300px;
  background-color: white;
  right: -25%;
  top: 20px
}

#wave:after {
  content: "";
  display: block;
  position: absolute;
  border-radius: 100%;
  width: 100%;
  height: 300px;
  background-color: black;
  left: -25%;
  top: -240px;
}
<div id="wave-container">
  <div id="wave">
  </div>
</div>
Dye answered 26/2, 2018 at 11:53 Comment(2)
Without inspecting the code too deeply, this seems better than the currently accepted answer, which for me (Firefox 61.0a1) displays a few straight horizontal pixels in the middle.Agglutinate
Change the colors and you'll see :)Dye
A
4

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.

 

function PolyCalc(f /*a function(t)  from [0, infinity) => [0, 1]*/, 
                  s, /*a slice function(y, i) from y [0,1] => [0, 1], with slice index, i, in [0, n]*/
									w /*window size in seconds*/,
                  n /*sample size*/,
                  o /*orientation => left/right/top/bottom - the 'flat edge' of the polygon*/ 
                  ) 
{
	this.polyStart = "polygon(";
  this.polyLeft = this.polyStart + "0% 0%, "; //starts in the top left corner
  this.polyRight = this.polyStart + "100% 0%, "; //starts in the top right corner
  this.polyTop = this.polyStart + "0% 0%, "; // starts in the top left corner
  this.polyBottom = this.polyStart + "0% 100%, ";//starts in the bottom left corner
  
  var self = this;
  self.mapFunc = s;
  this.func = f;
  this.window = w;
  this.count = n;
  var dt = w/n;  

  switch(o) {
    case "top":
      this.poly = this.polyTop; break;
    case "bottom":
      this.poly = this.polyBottom; break;
  	case "right":
    	this.poly = this.polyRight; break;
  	case "left":
  	default:
  		this.poly = this.polyLeft; break;
    }
    
  this.CalcPolygon = function(t) {
  	var p = this.poly;
    for (i = 0; i < this.count; i++) {
      x = 100 * i/(this.count-1.0);
      y = this.func(t + i*dt);
      if (typeof self.mapFunc !== 'undefined')
      	y=self.mapFunc(y, i);
      y*=100;
      switch(o) {
        case "top": 
          p += x + "% " + y + "%, "; break;
        case "bottom":
          p += x + "% " + (100-y) + "%, "; break;
      	case "right":
        	p += (100-y) + "% " + x + "%, "; break;
      	case "left":
        default:
        	p += y + "% " + x + "%, "; break;          
      }
    }
    
    switch(o) { 
      case "top":
        p += "100% 0%)"; break;
      case "bottom":
        p += "100% 100%)";
        break;
    	case "right":
      	p += "100% 100%)"; break;
    	case "left":
      default:
      	p += "0% 100%)"; break;
    }
    
    return p;
  }
};

var text = document.querySelector("#text");
var divs = document.querySelectorAll(".wave");
var freq=2*Math.PI; //angular frequency in radians/sec
var windowWidth = 1; //the time domain window which determines the range from [t, t+windowWidth] that will be evaluated to create the polygon
var sampleSize = 60;
divs.forEach(function(wave) {
  var loc = wave.classList[1];

  var polyCalc = new PolyCalc(
	  function(t) { //The time domain wave function
  	  return (Math.sin(freq * t) + 1)/2; //sine is [-1, -1], so we remap to [0,1]
    },
    function(y, i) { //slice function, takes the time domain result and the slice index and returns a new value in [0, 1]  
      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
    },
    windowWidth, //1 second, which with an angular frequency of 2pi rads/sec will produce one full period.
    sampleSize, //the number of samples to make, the larger the number, the smoother the curve, but the more pionts in the final polygon
    loc //the location
  );
  
    var polyText = polyCalc.CalcPolygon(0);
    wave.style.clipPath = polyText;
    wave.style.shapeOutside = polyText;
    wave.addEventListener("click",function(e) {document.querySelector("#polygon").innerText = polyText;});
  });

function MapRange(value, min, max, newMin, newMax) {
  return value * (newMax - newMin)/(max-min) + newMin;
}

//Animation - animate the wave by uncommenting this section
//Also demonstrates a slice function which uses the index of the slice to alter the output for a dampening effect.
/*
var t = 0;
var speed = 1/180;

var polyTop = document.querySelector(".top");

var polyTopCalc = new PolyCalc(
	  function(t) {
  	  return (Math.sin(freq * t) + 1)/2;
    },
    function(y, i) {       
      return MapRange(y, 0.0, 1.0, (sampleSize-i)/sampleSize, 1.0);
    },
    windowWidth, sampleSize, "top"
  );

function animate() {
		var polyT = polyTopCalc.CalcPolygon(t);    
    t+= speed;
    polyTop.style.clipPath = polyT;    
    requestAnimationFrame(animate);
}

requestAnimationFrame(animate);
*/
div div {
  padding:10px;
  /*overflow:scroll;*/
}

.left {
  height:100%;
  width:35%;
  float:left;
}

.right {
  height:200px;
  width:35%;
  float:right;
}

.top { 
  width:100%;
  height: 200px;  
}

.bottom {
  width:100%;
  height:200px;
}

.green {
  background:linear-gradient(to bottom, #b4ddb4 0%,#83c783 17%,#52b152 33%,#008a00 67%,#005700 83%,#002400 100%); 
} 

.mainContainer {
  width:100%;
  float:left;
}

#polygon {
  padding-left:20px;
  margin-left:20px;
  width:100%;
}
<div class="mainContainer">

  <div class="wave top green">
    Click to see the polygon CSS
  </div>
  
  <!--div class="wave left green">
  </div-->
  <!--div class="wave right green">
  </div-->  
  <!--div class="wave bottom green"></div-->  
</div>
<div id="polygon"></div>
Archaize answered 25/1, 2019 at 10:1 Comment(0)
M
0

The idea I have is to use two divs. The first div will have the svg set as a background image and the second one will have the same background color as the svg.

Below is an example:

.card {
    display: grid;

    width: 256px;
}

.card > .first {          
    padding: 32px 32px 48px 32px;  
    background: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 91.61'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%236585c3;%7D%3C/style%3E%3C/defs%3E%3Cpath class='cls-1' d='M0,420.39a459.6,459.6,0,0,0,123.1,27c78.32,6,118.8-11.72,182.59-14.61,48.24-2.18,118.44,3.73,206.31,43.82V512H0Z' transform='translate(0 -420.39)'/%3E%3C/svg%3E") no-repeat;
    background-position: bottom;
}

.card > .second {
    padding: 0 32px 32px 32px;
    background-color: #6585C3;
    color: white;
}
<div class="card">
    <div class="first">
        Lorem ipsum dolor sit amed
    </div>
    <div class="second">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
    </div>
</div>
Misinform answered 11/2, 2022 at 17:1 Comment(0)
M
0

I found I was often using "wave" and "inverted wave" shapes, so made a javascript function that will return the necessary polygon for use in clip-path attributes.

You can call the function with different values for the height of the wave, the offset (to move it up/down the container) and the flip parameter, to invert the wave form.

You can also adjust the inner variables for greater control of the wave form.

In the demo, I'm using css variables, to set the clip-path property of the ::before pseudo-element.

const wave = (wave, offset = 75, flip = false) => {

  const amplitude = 100;
  const frequency = 1;
  const phase = 100;
  const points = 50;

  const units = (2 * Math.PI * frequency) / points;
  const path = 'polygon(100% 100%, 0% 100% ';
  const invertedPath = 'polygon(100% 0%, 0% 0% ';

  let clipPathString = path;

  if (flip) {
    clipPathString = invertedPath;
  }

  const radPhase = (phase * Math.PI) / 180;

  for (let i = 0; i <= points; i++) {
    const val = offset + amplitude * Math.cos(i * units + radPhase);
    const valY = ((val / wave) * 100).toFixed(2);
    const valX = ((i * 100) / points).toFixed(2);
    clipPathString += `, ${valX}% ${valY}%`;
  }
  clipPathString += ')';

  return clipPathString;
}

const clipPath = wave(600, 500, true);

document.getElementById('wave').style.setProperty('--clipPath', clipPath)
#wave::before {
  content: "";
  position: absolute;
  background-color: #e0efe3;
  width: 100%;
  height: 50%;
  clip-path: var(--clipPath, 'none');
  z-index: -1;
}

#wave {
  padding: 5px;
  color: black;
}
<div id="wave">
  <h3>This is where you write your content</h3>
  <p>You can specify the wave height, offset and whether or not it is flipped / inverted.</p>
  <pre>
    // examples
    wave(10); // use defaults for offset and flip
    wave(100, 100, true); // specify all params 
  </pre>
</div>
Magen answered 24/8, 2023 at 15:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.