Search code examples
csssvgcss-shapes

Complex parallelogram button shapes


I was wondering if it was possible to do complex buttons like this in CSS3? I'm not sure where to start. I can do the trapezoidal like shape easily, but adding a border to that seems to be impossible. Am I missing something?

(PS: picture is form Deus Ex Human Revolution)

enter image description here


Solution

  • As you have yourself pointed out, generating the shape given in question is pretty easy and the below is a sample snippet on how to achieve it. The basic idea is to use a pseudo-element, add background to it and then skew it. The overflow: hidden on the parent would cut out the part of the shape that is not required and thus end up producing the shape.

    ul {
      list-style-type: none;
      margin: 0;
      padding: 0;
      list-style-position: inside;
    }
    li {
      position: relative;
      height: 40px;
      width: 300px;
      margin: 10px;
      padding-right: 30px;
      line-height: 40px;
      text-align: right;
      text-transform: uppercase;
      border-radius: 8px;
      color: crimson;
      overflow: hidden;
    }
    li:after {
      position: absolute;
      content: '';
      left: 0;
      top: 0;
      height: 100%;
      width: 100%;
      background: rgba(0, 0, 0, 0.5);
      transform: skew(45deg);
      z-index: -1;
    }
    
    /* just for demo */
    
    body {
      background: url(http://lorempixel.com/800/500/abstract/2);
    }
    *, *:after, *:before {
      box-sizing: border-box;
    }
    <ul>
      <li>Menu Text 1</li>
      <li>Menu Text 2</li>
      <li>Menu Text 3</li>
    </ul>

    However, adding borders to the shape that is created using this method is very tough and the fact that there needs to be a transparent area between the shape and its border makes it almost impossible.


    We can use clip-path like in the below snippet and it would have made it possible for a border to be added using a pseudo-element but this approach has the following drawbacks:

    • Pure CSS version of clip-path is not supported by Firefox and IE (all versions).
    • Even the inline SVG based clip-path would be supported only in Chrome and Firefox.
    • While the clip-path can be modified to produce that transparent area between the background and the border, it would make it ultra-complex and almost impossible to maintain.
    • Assigning a transparent or semi-transparent background color would cause problems because there is another element behind and so the colors would blend like in the below snippet.

    ul {
      list-style-type: none;
      margin: 0;
      padding: 0;
      list-style-position: inside;
    }
    li {
      position: relative;
      height: 40px;
      width: 300px;
      margin: 10px;
      padding-right: 30px;
      line-height: 40px;
      text-align: right;
      text-transform: uppercase;
      border-radius: 8px;
      background: crimson;
      color: crimson;
      -webkit-clip-path: polygon(0% 0%, 0% 50%, 10% 100%, 100% 100%, 100% 50%, 90% 0%);
      clip-path: polygon(0% 0%, 0% 50%, 10% 100%, 100% 100%, 100% 50%, 90% 0%);
    }
    li:after {
      position: absolute;
      content: '';
      height: calc(100% - 4px);
      width: calc(100% - 4px);
      left: 2px;
      top: 2px;
      border-radius: 7px;
      background: rgba(0, 0, 0, 0.5);
      -webkit-clip-path: polygon(0% 0%, 0% calc(50% - 1px), 10% 100%, 100% 100%, 100% calc(50% + 1px), 90% 0%);
      clip-path: polygon(0% 0%, 0% calc(50% - 1px), 10% 100%, 100% 100%, 100% calc(50% + 1px), 90% 0%);
      z-index: -1;
    }
    
    /* just for demo */
    
    body {
      background: url(http://lorempixel.com/800/500/abstract/2);
    }
    *, *:after, *:before {
      box-sizing: border-box;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <ul>
      <li>Menu Text 1</li>
      <li>Menu Text 2</li>
      <li>Menu Text 3</li>
    </ul>


    Given all the above, I would suggest you to use SVG for producing such complex shapes. With SVG, we can use the path elements to create both the border and the fill for the shape.

    ul {
      list-style-type: none;
      margin: 0;
      padding: 0;
      list-style-position: inside;
    }
    li {
      position: relative;
      height: 40px;
      width: 300px;
      margin: 10px;
      padding-right: 30px;
      line-height: 40px;
      text-align: right;
      text-transform: uppercase;
      border-radius: 8px;
      color: crimson;
    }
    li.active {
      width: 350px;
      height: 50px;
      line-height: 50px;
    }
    svg #fill {
      fill: rgba(0, 0, 0, 0.5);
    }
    svg #border {
      stroke: crimson;
      stroke-width: 2;
      fill: transparent;
    }
    li svg {
      position: absolute;
      top: 0px;
      left: 0px;
      height: 100%;
      width: 100%;
      z-index: -1;
    }
    
    /* just for demo */
    
    body {
      background: url(http://lorempixel.com/800/500/abstract/2);
    }
    *, *:after, *:before {
      box-sizing: border-box;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
    <svg viewBox='0 0 300 40' height='0' width='0'>
      <defs>
        <g id='shape'>
          <path d='M8,0 A8,8 0 0,0 0,8 L0,20 30,40 292,40 A8,8 0 0,0 300,32 L300,20 270,0z' id='fill' />
        </g>
        <g id='shape-bordered'>
          <path d='M9,1 A8,8 0 0,0 1,9 L1,20 30,39 292,39 A8,8 0 0,0 299,32 L299,20 270,1z' id='border' />
          <path d='M10,4 A7,7 0 0,0 4,6 L4,19 31,36 290,36 A5,5 0 0,0 296,34 L296,21 269,4z' id='fill' />
        </g>
      </defs>
    </svg>
    <ul>
      <li class='active'>
        <svg viewBox='0 0 300 40' preserveAspectRatio='none'>
          <use xlink:href='#shape-bordered' />
        </svg>Menu Text 1</li>
      <li>
        <svg viewBox='0 0 300 40' preserveAspectRatio='none' vector-effect='non-scaling-stroke'>
          <use xlink:href='#shape' />
        </svg>Menu Text 2</li>
      <li>
        <svg viewBox='0 0 300 40' preserveAspectRatio='none'>
          <use xlink:href='#shape' />
        </svg>Menu Text 3</li>
    </ul>

    (The SVG could probably be tuned further. The above snippet is only a sample to illustrate what can be achieved.)