3

I'm trying to create a hexagonal border to a button. My strategy is to make the container element a couple of pixels larger than the button, and use the same clip-mask on both elements. The difference in size should create a border effect. This works well in Firefox and Chrome, but not in Safari.

document.getElementById("button").addEventListener("click", function(){alert("foo"); return false;});
div {
  width: 9rem;
  height: 8rem;
  position: relative;
  clip-path: url("#hexagon");
  -webkit-clip-path: url("#hexagon");
  background-color: #e2e3e5;
}

button {
  position: absolute;
  cursor: pointer;
  top: 2px;
  left: 2px;
  padding: calc(8rem * 0.1) calc(9rem * 0.2);
  width: calc(9rem - 4px);
  height: calc(8rem - 4px);
  clip-path: url("#hexagon");
  -webkit-clip-path: url("#hexagon");
  background-color: white;
  border: none;
}
<div id="div">
<button id="button">The button</button>
</div>

<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <clipPath id="hexagon" clipPathUnits="objectBoundingBox">
      <polygon points=".25 0, .75 0, 1 .5, .75 1, .25 1, 0 .5"/>
    </clipPath>
  </defs>
</svg>

Without -webkit-clip-path the border was a rectangle in Safari. After adding it, the width of the resulting hexagon is much greater than in the other browsers. It appears the clip path is covering much more of the button than it should be. Is there any way to work around this so that it works well in Safari?

Firefox: Button in Firefox Safari: Button in Safari

miken32
  • 42,008
  • 16
  • 111
  • 154

2 Answers2

5

Instead of using clip-path: url() you can use clip-path:polygon() I've used your points multiplied by 10 and offset with 15 on x and 5 on y. Also I'm scaling the button since the "border" was barely visible.

document.getElementById("button").addEventListener("click", function(){alert("foo"); return false;});
div {
  width: 8rem;
  height: 6.6rem;
  position: relative;
  background-color: #ccc;
  -webkit-clip-path:polygon(40px 5px, 90px 5px, 115px 55px, 90px 105px,40px 105px, 15px 55px);
  clip-path:polygon(40px 5px, 90px 5px, 115px 55px, 90px 105px,40px 105px, 15px 55px);
}

button {
  position: absolute;
  cursor: pointer;
  text-align:center;
  width: 8rem;
  height: 6.6rem;
  -webkit-clip-path:polygon(40px 5px, 90px 5px, 115px 55px, 90px 105px,40px 105px, 15px 55px);
  clip-path:polygon(40px 5px, 90px 5px, 115px 55px, 90px 105px,40px 105px, 15px 55px);
  background-color: #fff;
  border:none;
  transform:scale(.97);
}
<div id="div">
<button id="button">The button</button>
</div>

Yet another option would be using an svg element instead of a button. Again - for the hexagon I'm using your points multiplied by 10. I personally prefer this option.

document.getElementById("button").addEventListener("click", function(){alert("foo"); return false;});
#button{cursor:pointer}
<svg viewBox="-1 -1 102 102" width="6rem" role="button">
 <polygon id="button" points="25 0, 75 0, 100 50, 75 100, 25 100, 0 50" stroke="#ccc" fill="none" pointer-events="all"/>
  <text x="50" y="50" text-anchor="middle" dominant-baseline="middle" font-size="14" pointer-events="none">the Button</text>
</svg>

Alternatively you may use this points:

document.getElementById("button").addEventListener("click", function(){alert("foo"); return false;});
#button{cursor:pointer}
<svg viewBox = "-50 -50 100 100" width="6rem" role="button">
  <polygon id="button" fill="none" stroke="#ccc" pointer-events="all" points = "48,0 24,41.57 -24,41.57 -48,0 -24,-41.57 24,-41.57 48,0" />

  <text text-anchor="middle" dominant-baseline="middle" font-size="14" pointer-events="none">the Button</text>
</svg>
enxaneta
  • 31,608
  • 5
  • 29
  • 42
  • This looks promising. I'd rather use a ` – miken32 Aug 14 '19 at 15:49
  • I thought you may say this. That's why I've added a [role="button"](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role) to the svg element. – enxaneta Aug 14 '19 at 15:54
  • I think you may like this answer about the `role` attribute: https://stackoverflow.com/questions/10403138/what-is-the-purpose-of-the-role-attribute-in-html – enxaneta Aug 14 '19 at 15:59
  • 1
    Yes, I'm familiar with that, but I don't know that UAs, especially things like screen readers, would be able to correctly identify the `` element as a label, etc. I do agree the SVG is a cleaner and more elegant solution, but will probably go with your first one. – miken32 Aug 14 '19 at 16:06
  • 2
    `clip-path: polygon()` can actually use percentages, which makes things much easier than pixel values. – miken32 Aug 14 '19 at 17:09
1

Thanks to enxaneta who earned their bounty by showing me clip-path: polygon. Since this takes percentages, it's super easy to push out a hexagonal shape. Still curious about what was happening with the SVG-based path, but this simple change to my original code works great:

document.getElementById("button").addEventListener("click", function() {
  alert("foo");
  return false;
});
div {
  width: 9rem;
  height: 8rem;
  position: relative;
  clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0% 50%, 25% 0);
  -webkit-clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0% 50%, 25% 0);
  background-color: #e2e3e5;
}

button {
  position: absolute;
  cursor: pointer;
  top: 2px;
  left: 2px;
  padding: calc(8rem * 0.1) calc(9rem * 0.2);
  width: calc(9rem - 4px);
  height: calc(8rem - 4px);
  clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0% 50%, 25% 0);
  -webkit-clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0% 50%, 25% 0);
  background-color: white;
  border: none;
}
<div id="div">
  <button id="button">The button</button>
</div>
adiga
  • 34,372
  • 9
  • 61
  • 83
miken32
  • 42,008
  • 16
  • 111
  • 154