Search code examples
htmlcsscss-shapes

How to create a curved line with gradient?


I'm trying to insert a curve in the middle of a div, so i can achieve this result:

CSS curved line

This is what i did so far.

.back{
  background-color: grey;
  width: 100%;
  height: 200px;
  display: inline-grid;
  align-items: center;
  overflow: hidden;
}

.line{
  height: 3px;
  background: linear-gradient(to right, yellow, purple, blue, green, red, orange);
  transform: rotate(-10deg);
}
<div class="back">
  <div class="line"></div>
</div>


Solution

  • Without gradient and with transparency you can consider pseudo element and border-radius. Each pseudo element will create half the curve and you join them to create the illusion of a continuous curve.

    .box {
      height:150px;
      margin:10px 0;
      position:relative;
      overflow:hidden;
      z-index:0;
      --l:3px;  /* Thickness of the curve */
      --r:90px; /* The curve radius */
      --w:60%;  /* The width of the left part (i.e distance of the curve from the left) */
      --c:red;  /* Color of the curve*/
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      z-index:-1;
      height:calc(50% - var(--l)); /* each one will take half the height minus border width*/
      border-style:solid;
      border-color:var(--c);
    }
    /* Left part */
    .box:before {
       bottom:0;
       left:0;
       width:var(--w);
       border-width:0 var(--l) var(--l) 0; /* Right & Bottom*/
       border-radius:0 0 var(--r) 0; /* Bottom-right*/
    }
    /* Right part*/
    .box:after {
       top:0;
       right:calc(-1 * var(--l)); /* Move slightly to the right to have a perfect join */
       width:calc(100% - var(--w));
       border-width:var(--l) 0 0 var(--l); /* Top & Left */
       border-radius:var(--r) 0 0 0; /* Top-Left*/
    }
    
    body {
     background:pink;
    }
    <div class="box">
    </div>
    
    <div class="box" style="--r:40px;--l:5px;--w:70%;--c:green">
    </div>
    
    <div class="box" style="--r:100px;--l:2px;--w:100px;--c:blue">
    </div>


    With gradient and without transparency you can rely on mulitple background coloration to approximate it. The idea is to have the gradient you want below and you cover it with other layers to keep only the curve visible:

    .box {
      --w:4px;
      height:150px;
      position:relative;
      z-index:0;
      background:
        radial-gradient(farthest-side at bottom right,grey calc(100% - var(--w) + 1px),transparent calc(100% - var(--w) + 1px) 99%,grey) top 0 right calc(40% - 0.6*56px + var(--w)/2)/54px 50%,
        radial-gradient(farthest-side at top left,grey calc(100% - var(--w) + 1px),transparent calc(100% - var(--w) + 1px) 99%,grey)  bottom 0 left calc(60% - 0.4*56px + var(--w)/2)/54px 50%,
        
        
        linear-gradient(grey,grey) bottom right/calc(40% - 50px)  calc(100% - var(--w)),
        linear-gradient(grey,grey) bottom right/calc(40% - var(--w)/2 + 1px) 50%,
        linear-gradient(grey,grey) top    left /calc(60% - 50px) calc(100% - var(--w)),
        linear-gradient(grey,grey) top    left /calc(60% - var(--w)/2 + 1px) 50%,
        
        
        linear-gradient(to right, yellow, purple, blue, green, red, orange);
      background-repeat:no-repeat;
      border-top:10px solid grey;
      border-bottom:10px solid grey;
    }
    <div class="box"></div>
    
    <div class="box" style="--w:6px"></div>

    This one is a bit tricky but you can change the coloration of each gradient to better identify each layer.

    .box {
      --w:4px;
      height:150px;
      position:relative;
      z-index:0;
      background:
        radial-gradient(farthest-side at bottom right,pink calc(100% - var(--w) + 1px),transparent calc(100% - var(--w) + 1px) 99%,red) top 0 right calc(40% - 0.6*56px + var(--w)/2)/54px 50%,
        radial-gradient(farthest-side at top left,yellow calc(100% - var(--w) + 1px),transparent calc(100% - var(--w) + 1px) 99%,orange)  bottom 0 left calc(60% - 0.4*56px + var(--w)/2)/54px 50%,
        
        
        linear-gradient(green,green) bottom right/calc(40% - 50px)  calc(100% - var(--w)),
        linear-gradient(purple,purple) bottom right/calc(40% - var(--w)/2 + 1px) 50%,
        linear-gradient(blue,blue) top    left /calc(60% - 50px) calc(100% - var(--w)),
        linear-gradient(black,black) top    left /calc(60% - var(--w)/2 + 1px) 50%,
        
        
        /*linear-gradient(to right, yellow, purple, blue, green, red, orange)*/
        white;
      background-repeat:no-repeat;
      border-top:10px solid grey;
      border-bottom:10px solid grey;
    }
    <div class="box"></div>
    
    <div class="box" style="--w:6px"></div>

    Related question to get more details about the different values: Using percentage values with background-position on a linear-gradient


    Another idea (based on the first code) to have gradient but still without transparency:

    .box {
      height:150px;
      margin:10px 0;
      position:relative;
      overflow:hidden;
      z-index:0;
      --l:3px;  /* Thickness of the curve */
      --r:90px; /* The curve radius */
      --c:to right, yellow, purple, blue, green, red, orange;  /* Color of the curve*/
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      z-index:-1;
      height:calc(50% - var(--l));
      border-style:solid;
      border-color:transparent;
      background:
        linear-gradient(pink,pink) padding-box, /* The same as the main background only on the padding-box*/
        linear-gradient(var(--c)) border-box;   /* Main coloration will cover the border-box */
    }
    .box:before {
       bottom:0;
       left:0;
       width:40%;
       border-width:0 var(--l) var(--l) 0;
       border-radius:0 0 var(--r) 0;
       background-size:calc(100/40 * 100%) 200%; /* 100/X where X is the width*/
       background-position:left bottom;
    }
    .box:after {
       top:0;
       right:calc(-1 * var(--l));
       width:60%;
       border-width:var(--l) 0 0 var(--l);
       border-radius:var(--r) 0 0 0;
       background-size:calc(100/60 * 100%) 200%; /* 100/X where X is the width*/
       background-position:right top;
    }
    
    body {
     background:pink;
    }
    <div class="box">
    </div>
    
    <div class="box" style="--r:40px;--l:10px;--c:to bottom,red,yellow,green">
    </div>
    
    <div class="box" style="--r:100px;--l:2px;--c:45deg,purple,#000,orange">
    </div>

    And we add mask to be above to get transparency:

    .box {
      height:150px;
      margin:10px 0;
      position:relative;
      overflow:hidden;
      z-index:0;
      --l:3px;  /* Thickness of the curve */
      --r:90px; /* The curve radius */
      --c:linear-gradient(to right, yellow, purple, blue, green, red, orange);  /* Color of the curve*/
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      z-index:-1;
      height:calc(50% - var(--l));
      border-style:solid;
      border-color:transparent;
      background: var(--c) border-box; 
      -webkit-mask:
        linear-gradient(#fff 0 0) padding-box,
        linear-gradient(#fff 0 0);
      -webkit-mask-composite:destination-out;
      mask-composite:exclude;
    }
    .box:before {
       bottom:0;
       left:0;
       width:40%;
       border-width:0 var(--l) var(--l) 0;
       border-radius:0 0 var(--r) 0;
       background-size:calc(100/40 * 100%) 200%; /* 100/X where X is the width*/
       background-position:left bottom;
    }
    .box:after {
       top:0;
       right:calc(-1 * var(--l));
       width:60%;
       border-width:var(--l) 0 0 var(--l);
       border-radius:var(--r) 0 0 0;
       background-size:calc(100/60 * 100%) 200%; /* 100/X where X is the width*/
       background-position:right top;
    }
    
    body {
     background:pink;
    }
    <div class="box">
    </div>
    
    <div class="box" style="--r:40px;--l:10px;--c:linear-gradient(to bottom,red,yellow,green)">
    </div>
    
    <div class="box" style="--r:100px;--l:2px;--c:linear-gradient(45deg,purple,#000,orange)">
    </div>