Search code examples
htmlcsscss-shapes

How to achieve a responsive ribbon shape in CSS?


enter image description here

Trying to achieve this with css. I've achieved in doing this but this breaks if my label (CAREER) is longer the size of the div. If its longer then the content wraps and the height of the div increases. But the left ribbon cut does not do that responsively. Can someone suggest a better approach?

.custom-tag-container {
  border: 1px solid;
  margin: auto;
  display: flex;
  align-items: stretch;
  border-color: green green green transparent;
  padding: 4px !important;
}

.custom-tag-container>p {
  color: green;
  font-weight: bold;
  flex: 1;
  margin: auto;
}

#triangle-left {
  height: 25px;
  width: 25px;
  background: transparent;
  transform: rotateZ(45deg) translateX(-12.5px) translateY(12.5px);
  border: 1px solid;
  border-color: green green transparent transparent;
}
<div class="custom-tag-container">
  <div id="triangle-left" />
  <p>Hello Worldsm</p>
</div>

JS Fiddle


Solution

  • Here is an idea that rely on skew transformation where it will be responsive and you will have transparency:

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      position:relative;
      margin:5px;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      left:0;
      height:50%;
      width:3px;
      background:green;
    }
    .box:before {
      top:0;
      transform:skew(45deg);
      transform-origin:top;
    }
    .box:after {
      bottom:0;
      transform:skew(-45deg);
      transform-origin:bottom;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>

    Ribbon Edge CSS

    If you want to keep the same width for the arrow, you can consider a different idea using background:

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      position:relative;
      margin:5px;
      overflow:hidden;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      left:0;
      top:0;
      height:50%;
      width:30px;
      background:
        linear-gradient(to top right,
          transparent calc(50% - 4px),green,transparent 50%) 
          bottom/100% calc(100% + 4px);
    }
    .box:after {
      transform:scaleY(-1);
      transform-origin:bottom;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>

    and with SVG too

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      position:relative;
      margin:5px;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      left:0;
      top:0;
      height:50%;
      width:30px;
      background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" preserveAspectRatio="none"><line x1="0" y1="-1" x2="15" y2="16" stroke="green"  /></svg>') 0 0/100% 100%;
    }
    .box:after {
      transform:scaleY(-1);
      transform-origin:bottom;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>

    enter image description here

    You can also optimize the code and do it without pseudo element:

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      margin:5px;
      background:
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" preserveAspectRatio="none"><line x1="0" y1="-1" x2="15" y2="16" stroke="green"  /></svg>') top left/30px 50%,
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" preserveAspectRatio="none"><line x1="0" y1="16" x2="15" y2="-1" stroke="green"  /></svg>') bottom left/30px 50%;
      background-repeat:no-repeat;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>

    The same SVG can be used to obtain the first result where the arrow width is also dynamic. The trick is to only define the height and preserve the ratio:

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      margin:5px;
      background:
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15"><line x1="0" y1="-1" x2="15" y2="16" stroke="green"  /></svg>') top left/auto 50%,
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15"><line x1="0" y1="16" x2="15" y2="-1" stroke="green"  /></svg>') bottom left/auto 50%;
      background-repeat:no-repeat;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>


    You can easily add a coloration inside too.

    With skew transformation:

    .box {
      height: 50px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      position:relative;
      overflow:hidden;
      z-index:0;
      margin:5px;
    }
    .box:before,
    .box:after{
      content:"";
      position:absolute;
      z-index:-1;
      left:0;
      right:0;
      height:50%;
      border-left:3px solid green;
      background:yellow;
    }
    .box:before {
      top:0;
      transform:skew(45deg);
      transform-origin:top;
    }
    .box:after {
      bottom:0;
      transform:skew(-45deg);
      transform-origin:bottom;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>

    With the SVG background

    .box {
      height: 50px;
      padding-left:30px;
      border: 2px solid green;
      border-left: 0;
      border-radius:0 5px 5px 0;
      margin:5px;
      background:
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" preserveAspectRatio="none"><polygon points="16,17 16,-2 0,-1 " fill="yellow" stroke="green" /></svg>') top left/30px 50%,
       url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 15 15" preserveAspectRatio="none"><polygon points="16,16 16,-2 0,16 " fill="yellow" stroke="green" /></svg>') bottom left/30px 50%,
       yellow content-box;
      background-repeat:no-repeat;
    }
    <div class="box"></div>
    
    <div class="box" style="height:80px"></div>