Search code examples
htmlcsscss-shapeslinear-gradients

How to create a curve between two gradient using CSS?


I have two divs with distinct gradient background and I need to create a S-Shape curve between them.

enter image description here

Here's the example fiddle for gradient divs: https://jsfiddle.net/JerryGoyal/rjyfc46c/2/

#section1{
    height:200px;
    background: linear-gradient(to bottom right, #ad3, #add);
}
#section2{
    height:200px;
    background: linear-gradient(to bottom right, #de350b, #0065ff);
}
<div id="section1"></div>
<div id="section2"></div>

There are couple of things which crossed my mind but:

- svg: don't know how to handle other gradient div.
- border-radius: failed to get truly S-like curve plus it gets ugly when I resize the screen.
- clip-path: not supported by some browsers https://caniuse.com/css-clip-path
- png image: nope! needs to be dynamic content.

any help would be appreciated!


P.S: a must read for future readers: https://css-tricks.com/creating-non-rectangular-headers/


Solution

  • Here is a solution using linearGradient with SVG.

    .container {
      width: 500px;
      height: 200px;
      background:linear-gradient(to bottom right, #de350b, #0065ff);
    }
    svg {
      width:100%;
    }
    <div class="container">
      <svg mlns='http://www.w3.org/2000/svg' viewBox="0 0 64 64">
        <defs>
        <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#ad3" />
          <stop offset="100%" stop-color="#add" />
        </linearGradient>
      </defs>
        <path d='M0 10 C30 28 38 0 64 10 L64 0 L0 0 Z'  fill="url(#grad)"/>
      </svg>
    </div>

    SVG curve gradients

    Here is also a useful online tool to easily edit the shape (simply append the path to the URL to edit ithttp://jxnblk.com/paths/?d=M0 10 C30 28 38 0 64 10 L64 0 L0 0 Z )


    Another idea with the same SVG used as a background so you can easily handle content above it:

    .container {
      width: 500px;
      height: 200px;
      background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="500" ><defs><linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="%23ad3" /><stop offset="100%" stop-color="%23add" /></linearGradient></defs><path d="M0 10 C30 28 38 0 64 10 L64 0 L0 0 Z"  fill="url(%23grad)"/></svg>'), 
      linear-gradient(to bottom right, #de350b, #0065ff);
      
      display:flex;
      justify-content:space-around;
      align-items:center;
      flex-direction:column;
      color:#fff;
    }
    <div class="container">
    <p>TOP</p>
    <p>BOTTOM</p>
    </div>


    In case you want a pure CSS solution with no SVG involved here is one using mask and radial-gradient:

    .box {
      height:200px;
      position:relative;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      top:0;
      left:0;
      right:0;
      height:100%;
      background: linear-gradient(to bottom right, #ad3, #add);
    }
    .box:after {
      -webkit-mask:
        radial-gradient(100% 80% at top   ,white 79.5%,transparent 80%) left,
        radial-gradient(100% 80% at bottom,transparent 79.5%,white 80%) right;
      mask:
        radial-gradient(100% 80% at top   ,white 79.5%,transparent 80%) left,
        radial-gradient(100% 80% at bottom,transparent 79.5%,white 80%) right;
      -webkit-mask-size:50.1% 100%;
      -webkit-mask-repeat:no-repeat;
      mask-size:50.1% 100%;
      mask-repeat:no-repeat;
      background:linear-gradient(to bottom right, #de350b, #0065ff);
    }
    <div class="box">
    
    </div>

    CSS curve linear gradient mask

    Adjust the different values to control the curve. The trick is to make sure both are the same and start at the same point to create a contiguous shape. You can introduce CSS variables to easily control this:

    .box {
      height:200px;
      margin:10px;
      position:relative;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      top:0;
      left:0;
      right:0;
      height:100%;
      background: linear-gradient(to bottom right, #ad3, #add);
    }
    .box:after {
      -webkit-mask:
        radial-gradient(var(--r1,100%) var(--r2,80%) at top   ,white 79.5%,transparent 80%) left,
        radial-gradient(var(--r1,100%) var(--r2,80%) at bottom,transparent 79.5%,white 80%) right;
      mask:
        radial-gradient(var(--r1,100%) var(--r2,80%) at top   ,white 79.5%,transparent 80%) left,
        radial-gradient(var(--r1,100%) var(--r2,80%) at bottom,transparent 79.5%,white 80%) right;
      -webkit-mask-size:50.1% 100%;
      -webkit-mask-repeat:no-repeat;
      mask-size:50.1% 100%;
      mask-repeat:no-repeat;
      background:linear-gradient(to bottom right, #de350b, #0065ff);
    }
    <div class="box">
    
    </div>
    
    <div class="box" style="--r1:82%;--r2:97%">
    
    </div>
    
    <div class="box" style="--r1:126%;--r2:72%">
    
    </div>

    CSS mask gradient curve

    To make things funnier we can add a border between both gradient following the curve where we can place another gradient!

    .box {
      height:200px;
      margin:10px;
      position:relative;
      background:linear-gradient(to right,blue,black);
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      left:0;
      right:0;
      height:calc(100% - var(--b,10px)); /*control the gap here*/
      -webkit-mask:
        radial-gradient(var(--r1,100%) var(--r2,80%) at var(--p1,top)   ,white 79.5%,transparent 80%) var(--d1,right),
        radial-gradient(var(--r1,100%) var(--r2,80%) at var(--p2,bottom),transparent 79.5%,white 80%) var(--d2,left);
      mask:
        radial-gradient(var(--r1,100%) var(--r2,80%) at var(--p1,top)   ,white 79.5%,transparent 80%) var(--d1,right),
        radial-gradient(var(--r1,100%) var(--r2,80%) at var(--p2,bottom),transparent 79.5%,white 80%) var(--d2,left);
      -webkit-mask-size:50.1% 100%;
      -webkit-mask-repeat:no-repeat;
      mask-size:50.1% 100%;
      mask-repeat:no-repeat;
    }
    .box:before {
      top:0;
      background:linear-gradient(to bottom right, #de350b, #0065ff);
    }
    .box:after {
      bottom:0;
      background: linear-gradient(to bottom right, #ad3, #add);
      --p1:bottom;
      --p2:top;
      --d1:left;
      --d2:right;
    }
    <div class="box">
    
    </div>
    
    <div class="box" style="--r1:82%;--r2:97%;--b:20px">
    
    </div>
    
    <div class="box" style="--r1:126%;--r2:72%;--b:5px">
    
    </div>

    CSS curve shape multiple gradients