2

I have created a shape with 5 corners using polygon as shown below.

enter image description here

This is the css needed to make it:

div {
    width: 280px;
    height: 280px;
    background: #1e90ff;
    -webkit-clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
    clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
    border: 3px solid black;
    border-bottom-left-radius: 15px;
    border-top-left-radius: 15px;
}

Unfortunately I cannot get it to add a black border all around the shape (it is missing on the right side). How would I do that?

I have created a fiddle here.

brinch
  • 2,544
  • 7
  • 33
  • 55

2 Answers2

5

The border property goes around the outside of an element, and the clip-path property applies a mask to an element. So, as far as other CSS rules are concerned, you're still dealing with a rectangle. Because of this, you can't simply apply a border.

It's not impossible to get very close to the effect you want, however. Using (and abusing) CSS pseudo-elements, borders, and filters you can create the same shape plus an outline with only a single HTML div.

div {
  position: relative;
  width: 210px;
  height: 280px;
  background: #1e90ff;
  border-bottom-left-radius: 15px;
  border-top-left-radius: 15px;
  filter:
    drop-shadow( 0px  3px 0 black)
    drop-shadow( 0px -3px 0 black)
    drop-shadow( 3px  0px 0 black)
    drop-shadow(-3px  0px 0 black);
  -webkit-filter:
    drop-shadow( 0px  3px 0 black)
    drop-shadow( 0px -3px 0 black)
    drop-shadow( 3px  0px 0 black)
    drop-shadow(-3px  0px 0 black);
}
div::after {
  position: absolute;
  display: block;
  content: '';
  width: 0;
  height: 0;
  left: 210px;
  top: 0;
  border-left: 70px solid #1e90ff;
  border-right: 70px solid transparent;
  border-top: 140px solid transparent;
  border-bottom: 140px solid transparent;
}
<div>
</div>

So, what's going on here?

The main div element is just the rectangular portion of the shape (with rounded top-left and bottom-left corners). It also has an ::after pseudo-element which creates the triangle shape. The triangle shape is done using the CSS border-triangle hack.

Okay, so that creates the shape without having to mask off any of the element, but a border still can't be applied directly, since I've already (ab)used the border to create the triangle. Using a CSS outline or box-shadow would be the next logical choice, but both properties go all the way around the element's bounding-box... ignoring features like rounded corners or hacky transparent borders.

Enter the CSS filter property. This provides a family of post-processing filters. The important thing to note is that they treat the element as a transparency mask when being applied, rather than respecting the element's bounding-box... and there is a drop-shadow filter!

Unfortunately, it's not quite as flexible as the box-shadow property in that it doesn't have a "spread" parameter which can be used to create solid shapes that are larger than the element. To get around this, we need to cast a drop-shadow in every direction: up, down, right, and left. That's where this monstrosity comes in:

filter:
  drop-shadow( 0px  3px 0 black)
  drop-shadow( 0px -3px 0 black)
  drop-shadow( 3px  0px 0 black)
  drop-shadow(-3px  0px 0 black);

Ta-da!

Expanding on this: this works on any shape you can manage to create (as long as it done without clipping). Just apply a drop-shadow filter to the element, or to a parent/wrapper div (if it's a multi-element shape) to get a border.

Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
2

I'm on a bit of a pseudo kick here, but You can always recreate the element with a black color and throw it behind the original, giving the impression of an outline. Suitable for most purposes, the proposal is demonstrated in this codepen: http://codepen.io/anon/pen/MeJorM

CSS in the codepen has been changed up a bit. Your original is ".orig".

div.orig {
  position:relative;
    width: 280px;
    height: 280px;
    background: #1e90ff;
    -webkit-clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  border: 1px solid black;
  border-bottom-left-radius: 15px;
  border-top-left-radius: 15px;
  display:block;
}
div.abc {
  position:relative;
    width: 280px;
    height: 280px;
    background: #000;
    -webkit-clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  border: 1px solid black;
  border-bottom-left-radius: 15px;
  border-top-left-radius: 15px;
  display:block;
}
div.abc div.def{
  top:-0.02em;
  left:-0.2em;
  position:absolute;
  display:block;
    width: 278px;
    height: 280px;
    background: #1e90ff;
    -webkit-clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 0 48%, 0% 0%);
  border: 1px solid black;
  border-bottom-left-radius: 15px;
  border-top-left-radius: 15px;
}
MassDebates
  • 917
  • 6
  • 10
  • good solution. you could even use an `::after` pseudo-element to render the background one to avoid the need for creating a second div in the markup. – Woodrow Barlow Jun 24 '16 at 21:06
  • Yeah, I knew it could be done with pseudos (hence my "pseudo-kick" comment) but I wanted to show the proposal without them for the sake of browser support. – MassDebates Jun 27 '16 at 12:15