Elongated hexagon shaped button using only one element


I would like to make a button like these one just with CSS without using another element.

Button Image

enter image description here

Since the button has a border attached, I think I normally need both, the :before and :after elements to create just one arrow at one side. So to make one arrow at each side I would need another span element inside the link.

The second method I tried is the one you see below. But with this solution they are not properly centered and each side of the arrow is different in length.

Has someone a solution?

/* General Button Style */

.button {
  display: block;
  position: relative;
  background: #fff;
  width: 300px;
  height: 80px;
  line-height: 80px;
  text-align: center;
  font-size: 20px;
  text-decoration: none;
  text-transform: uppercase;
  color: #e04e5e;
  margin: 40px auto;
  font-family: Helvetica, Arial, sans-serif;
  box-sizing: border-box;
}
/* Button Border Style */

.button.border {
  border: 4px solid #e04e5e;
}
.button.border:hover {
  background: #e04e5e;
  color: #fff;
}
/* Button Ribbon-Outset Border Style */

.button.ribbon-outset.border:after,
.button.ribbon-outset.border:before {
  top: 50%;
  content: " ";
  height: 43px;
  width: 43px;
  position: absolute;
  pointer-events: none;
  background: #fff;
}
.button.ribbon-outset.border:after {
  left: -3px;
  margin-top: -40px;
  transform-origin: 0 0;
  box-sizing: border-box;
  border-bottom: 4px solid #e04e5e;
  border-left: 4px solid #e04e5e;
  transform: rotate(57.5deg) skew(30deg);
}
.button.ribbon-outset.border:before {
  right: -46px;
  margin-top: -40px;
  transform-origin: 0 0;
  box-sizing: border-box;
  border-top: 4px solid #e04e5e;
  border-right: 4px solid #e04e5e;
  transform: rotate(57.5deg) skew(30deg);
}
.button.ribbon-outset.border:hover:after {
  background: #e04e5e
}
.button.ribbon-outset.border:hover:before {
  background: #e04e5e
}
<a href="#" class="button ribbon-outset border">Click me!</a>

CodePen Demo


Answers:


Here is another alternate way to get this done with only one element.

This approach works like below:

  1. Two pseudo-elements :before and :after which are about half the size (including borders) of the main .button element. The height of each pseudo-element is 34px + 4px border on one side (top/bottom) and 2px on the other side.
  2. The top half of the shape is achieved using the :before element whereas the bottom half is achieved using the :after element.
  3. Using a rotateX with perspective to achieve the tilted effect and positioning to place the two elements such that they form the expected shape.

/* General Button Style */

.button {
  position: relative;
  display: block;
  background: transparent;
  width: 300px;
  height: 80px;
  line-height: 80px;
  text-align: center;
  font-size: 20px;
  text-decoration: none;
  text-transform: uppercase;
  color: #e04e5e;
  margin: 40px auto;
  font-family: Helvetica, Arial, sans-serif;
  box-sizing: border-box;
}
.button:before,
.button:after {
  position: absolute;
  content: '';
  width: 300px;
  left: 0px;
  height: 34px;
  z-index: -1;
}

.button:before {
  transform: perspective(15px) rotateX(3deg);
}
.button:after {
  top: 40px;
  transform: perspective(15px) rotateX(-3deg);
}

/* Button Border Style */

.button.border:before,
.button.border:after {
  border: 4px solid #e04e5e;
}
.button.border:before {
  border-bottom: none; /* to prevent the border-line showing up in the middle of the shape */
}
.button.border:after {
  border-top: none; /* to prevent the border-line showing up in the middle of the shape */
}

/* Button hover styles */

.button.border:hover:before,
.button.border:hover:after {
  background: #e04e5e;
}
.button.border:hover {
  color: #fff;
}
<!-- Library included to avoid browser prefixes -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>

<a href="#" class="button ribbon-outset border">Click me!</a>

Fixed Width Demo | Dynamic Width Demo

Output Screenshot:

enter image description here

This is tested in Chrome v24+, Firefox v19+, Opera v23+, Safari v5.1.7, IE v10.


As-is, this would degrade quite well in IE 8 and IE 9 into a square button with borders. However, due to the nullification of one border (border-bottom for :before and border-top for :after) it would leave a white area (resembling a strike-through line) in the middle. This can be overcome by adding a couple of IE < 10 specific styles using conditional comments like in this demo.

<!--[if IE]>
  <style>
    .button.border:after{
      top: 38px;
    }
    .button.border:hover:before, .button.border:hover:after {
      border-bottom: 4px solid #e04e5e;
    }
  </style>
<![endif]-->

Output Screenshot from IE 9 and IE 8:

enter image description here