91

I want to know if it is a possible to add border in my clip-path:polygon(); style or any another way to add border?

like : border:5px solid red;

CSS

.poligon {
  display: inline-block;
  position:relative;
  width:150px;
  height:150px;
  background: black;
  box-sizing:border-box;
  padding:55px;
}
.poligon img {
  display: inline-block;
  border:5px solid red;
  width:150px;
  height:150px;
  -webkit-clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
  -moz-clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
}

HTML

<div class="poligon">
  <img src="http://lorempixel.com/g/600/400/">
</div>
Harry
  • 87,580
  • 25
  • 202
  • 214

9 Answers9

126

Can border be applied to a clipped element along the clip path?

No, adding border property to the clipped element would not apply the borders along the clipped path because the border is applied to the original rectangle (or square) container before the clip-path is applied and so, it also gets clipped out. You can see this in the below snippet:

div {
  display: inline-block;
  height: 200px;
  width: 200px;
  border: 3px solid;
  background: darkseagreen;
}
div + div {
  -webkit-clip-path: polygon(50% 0%, 100% 100%, 100% 0%);
  clip-path: polygon(50% 0%, 100% 100%, 100% 0%);
}
<div></div>
<div></div>

Are there any alternate ways to create such a border effect?

We can mimic it by applying the same clip-path on a container element. The container element's background color will look as though it is the border of the inner element because both are clipped and the container has slightly higher dimensions than the inner element.

.poligon {
  display: inline-block;
  position: relative;
  width: 150px;
  height: 150px;
  background: red;
  box-sizing: border-box;
  -webkit-clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
  clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
}
.poligon img {
  position: absolute;
  top: 2px; /* equal to border thickness */
  left: 2px; /* equal to border thickness */
  width: 146px; /* container height - (border thickness * 2) */
  height: 146px; /* container height - (border thickness * 2) */
  -webkit-clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
  clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
}
<div class="poligon">
  <img src="https://picsum.photos/600/400">
</div>
supersan
  • 5,671
  • 3
  • 45
  • 64
Harry
  • 87,580
  • 25
  • 202
  • 214
  • 1
    thank you dear. So little question. The clip-path not supported mozilla ? Because my demo style is not working in firefox ? –  Aug 06 '15 at 11:19
  • 2
    @innovation: Yes, [Firefox at present supports only the `url()` syntax](http://caniuse.com/#feat=css-clip-path) and it needs an SVG (inline or external) as its input. You can easily convert your existing CSS `clip-path` polygon into a SVG equivalent. – Harry Aug 06 '15 at 11:20
  • 2
    @innovation: I have added a SVG version also to the answer. This would work in Firefox also. – Harry Aug 06 '15 at 13:56
  • Dear i need to know how did you do that? Because i made something like your answer but it looks like this. Please [CLICK](http://codepen.io/shadowman86/pen/pJxdzZ). So what i am trying to do. Please click for my other DEMO. I want to make first div in this [DEMO](http://codepen.io/shadowman86/pen/pJxdzZ) –  Aug 07 '15 at 16:13
  • Sorry, I did not understand what you mean. Both the demos look like the same to me. – Harry Aug 07 '15 at 16:23
  • sorry :) this is what i am trying Dear [PROBLEM](http://codepen.io/shadowman86/pen/OVqJNP?editors=110) –  Aug 07 '15 at 16:29
  • @innovation: Debugging that whole list of values is very tough. Your path co-ordinates are wrong and that's why the output is incorrect. You need to correctly convert the CSS polygon co-ordinates into its SVG equivalent (divide by 100). – Harry Aug 07 '15 at 16:33
  • but your answer css polygon co-ordinates into the SVG is like my incorrect demo. How did you do that? Can you tell me? –  Aug 07 '15 at 16:39
  • 1
    This will not work for anything with transparency in the image since it will put the border color in the transparency as well. – Floss Mar 07 '16 at 20:11
  • @Floss: The approach will still work but your path will have to be more complex. Instead of just producing a clip on the outer it should have a clip on the inner also. It is not impossible to achieve. – Harry Mar 07 '16 at 20:24
  • Its a good solution! I just came to simplyfy, you can use `clip-path: inherit;` inside `.poligon img` instead copy the same code, it will copy the parent value! ;D – KevynTD Apr 17 '22 at 18:06
40

Adding a Border to a Complex Clip Path With SVG Dilation Filter

Copy-Shrink Method Only Works In Simple Cases -- used in currently accepted answer

The currently accepted answer works by making a copy and shrinking it. This works for the example, but if you have a more complex shape (like text), it will not work. A better approach is to use dilation with a filter.

Shape Dilation Works on ANY Shape

A better approach is to use dilation with the feMorphology filter!!

Key aspects:

  • Create matching <image> and <rect> shapes of equal height and width
  • Clip both with the desired shape path/polygon
  • Use filter to dilate/enlarge the clipped rect to make a border

The filter radius= becomes the stand in for border thickness.

The result:

enter image description here

.clipper{
  clip-path: url(#clip_shape);
}
.dilate{
  filter: url("#dilate_shape");
}
<svg xmlns="http://www.w3.org/2000/svg" height="400" width="400">
 <defs>
  <clipPath id="clip_shape" clipPathUnits="objectBoundingBox">
    <text x="0" y=".8" lengthAdjust="spacing" font-weight="700" font-style="italic" font-size="1"> M </text>
  </clipPath>
   <filter id="dilate_shape">
      <feMorphology operator="dilate" in="SourceGraphic" radius="5" />
   </filter>
   
 </defs> 
 
 <g transform="translate(5,5)">
 <g class="dilate">
   <rect class="clipper" x=0 y=0 height="400px" width="400px" fill="lightgreen"></rect>
 </g>
 <image class="clipper" xlink:href="http://placekitten.com/400/300" height="400px" width="400px">
 </g>
</svg>
Community
  • 1
  • 1
Steve Ladavich
  • 3,472
  • 20
  • 27
31

Pseudo-Element

A nice way to do this would be with a pseudo-element like a ::before

Make exactly the same shape but slightly smaller which holds the main color you want and position it correctly and you get the border you want.

The below example isn't the correct shape but shows how to achieve this effect:

.shape {
  width: 400px;
  height: 40px;
  background-color: black;
  position: relative;
}
.shape:before {
  content: '';
  width: 398px;
  height: 38px;
  background: #00c000;
  display: block;
  position: absolute;
  top: 1px;
  left: 1px;
}
.shape, .shape:before {
  -webkit-clip-path: polygon(5% 0, 100% 0, 100% 100%, 0 100%, 0 100%);
  clip-path: polygon(5% 0, 100% 0, 100% 100%, 0 100%, 0 100%);
}
  
<div class="shape"></div>
Andy
  • 4,783
  • 2
  • 26
  • 51
Stewartside
  • 20,378
  • 12
  • 60
  • 81
  • 6
    Like most solutions to this problem, this one fails for any concave polygon. For simply convex polygons, it's fine, but the "inside" portions of a concave polygon are not served by this or any other "layered" effect. Unfortunately. – Dave Land Jul 10 '18 at 04:27
14

It is possible to use Drop-Shadow

  • The shape is entered only once
  • Size can be automatically set according to the content
  • The edge is outside the shape
  • One "Border Container" may contain more shapes

.arrowBg {
  filter: drop-shadow(1px 0px 0px black)
          drop-shadow(-1px 0px 0px black)
          drop-shadow(0px 1px 0px black)
          drop-shadow(0px -1px 0px black)
          drop-shadow(1px 1px 0px black)
          drop-shadow(-1px -1px 0px black)
          drop-shadow(-1px 1px 0px black)
          drop-shadow(1px -1px 0px black);
}
.arrow {
  background: #FFFF00;
  margin:20px;
  font-family: sans-serif;
  font-size:20px;
}
.arrowLeft {
  padding: 20px 20px 20px 40px;
  clip-path: polygon(40px 0%, 100% 0%, 100% 100%, 40px 100%, 0 50%);
}
.arrowRight{
  padding: 20px 40px 20px 20px;
  clip-path: polygon(calc(100% - 40px) 0%, 0 0, 0 100%, calc(100% - 40px) 100%, 100% 50%);
}
<br><br><br>
<span class="arrowBg">
  <span class="arrow arrowLeft">Go left</span>
  <span class="arrow arrowRight">Go go go right</span>
</span>

Sorry for my English

Robert Máslo
  • 208
  • 2
  • 5
  • Many thanks Robert. Just two additions. 1: applying background-color on inner element is crucial - I would emphasise this in the code as a comment (I've almost given up believing that my more complex svg mask is my failure's reason). 2: the combination of drop-shadows can be reduced and the blur value can be ignored: filter: drop-shadow(1px 1px black) drop-shadow(-1px -1px black) drop-shadow(0 1px black) drop-shadow(1px 0 black); – Rich Oct 22 '21 at 11:33
  • nice!! I think about drop-shadow but not understand it should be on the parent – pery mimon Jul 16 '22 at 21:59
  • Unfortunately, the solution fails with thin margins. I tried it with 0.5px, but that leads to uneven borders. But for simple shapes and margins >=1px a good solution. – mkours Jan 14 '23 at 09:23
12

Solution with pseudo element

I am write some simple code, use pseudo element - ::before, that I want to share.

I create the same shape-poligon, just bigger.

And so it seems that it has a border as you wanted (5px)

link to some nice clip-path: https://bennettfeely.com/clippy/

.poligon {
  height: 160px;
  width: 160px;
  background-color: black;
  clip-path: polygon(92.32051% 40%, 93.79385% 43.1596%, 94.69616% 46.52704%, 95% 50%, 94.69616% 53.47296%, 93.79385% 56.8404%, 92.32051% 60%, 79.82051% 81.65064%, 77.82089% 84.50639%, 75.35575% 86.97152%, 72.5% 88.97114%, 69.3404% 90.44449%, 65.97296% 91.34679%, 62.5% 91.65064%, 37.5% 91.65064%, 34.02704% 91.34679%, 30.6596% 90.44449%, 27.5% 88.97114%, 24.64425% 86.97152%, 22.17911% 84.50639%, 20.17949% 81.65064%, 7.67949% 60%, 6.20615% 56.8404%, 5.30384% 53.47296%, 5% 50%, 5.30384% 46.52704%, 6.20615% 43.1596%, 7.67949% 40%, 20.17949% 18.34936%, 22.17911% 15.49361%, 24.64425% 13.02848%, 27.5% 11.02886%, 30.6596% 9.55551%, 34.02704% 8.65321%, 37.5% 8.34936%, 62.5% 8.34936%, 65.97296% 8.65321%, 69.3404% 9.55551%, 72.5% 11.02886%, 75.35575% 13.02848%, 77.82089% 15.49361%, 79.82051% 18.34936%);
}

.poligon::before {
  content: "";
  display: block;
  position: relative;
  top: 5px;
  left: 5px;
  height: 150px;
  width: 150px;
  background-color: red;
  clip-path: inherit;
<div class="poligon"></div>

tnx "KevynTD" for your comment :)

Omer
  • 3,232
  • 1
  • 19
  • 19
  • 1
    Your answer is not clear. Please edit your first line – DjSh Jun 27 '19 at 15:27
  • 3
    Awesome solution! To simplify it you can use `clip-path: inherit;` inside `.poligon:before` instead copy the same value, it will copy the parent's value! ;D – KevynTD Apr 17 '22 at 18:11
7

Here's my solution:

#wrapper {
    width: fit-content;
    margin: auto;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 60% 90%, 50% 100%, 40% 90%, 0 100%);
    background: black;
    border: 10px solid black;
    /* or use padding instead of border: */
    /* padding: 10px; */
}
#test {
    width: 300px;
    height: 150px;
    margin: auto;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 60% 90%, 50% 100%, 40% 90%, 0 100%);
    background: red;
}
 <div id="wrapper">
  <div id=test></div>
 </div>

Using fit-content will size the wrapper equal to the test div. Since the box sizing of the wrapper is "content-box", its with includes additional padding + border; adding either will give you an extra border around the clipped region. As long as your clip has the same percentage values, you create a constant difference for this extra border.

If using border instead of padding to create the extra border, it must be the same color as the wrapper's background. A colored background is necessary, as it provides the border for the inner clipped region

Azmisov
  • 6,493
  • 7
  • 53
  • 70
Cyril
  • 177
  • 1
  • 7
  • I logged in just to say this is IMO **the best solution!!** Not only is it concise and works like a charm, it also makes sense semantically (setting border width) and works without any arbitrary margins or spacings. Absolute Props! – csstudent1418 Apr 19 '23 at 07:21
3

Here's how to do it.

<div class="screenshot"><img src="assets/img/tutorial/1.jpg"></div>

Just add duplicate the mask and add a padding to the parent.

.screenshot {
    mask: url(../assets/img/bubble.svg) center center no-repeat;
    background: white;
    padding: 10px;

    img {
        mask: url(../assets/img/bubble.svg) center center no-repeat;
    }
}
kinoli
  • 109
  • 2
  • 5
0

I have another solution to this..

This is what I'm working on: enter image description here

.top-angle-left {
    -webkit-clip-path: polygon(0 0, 100% 15%, 100% 100%, 0 100%);
    clip-path: polygon(0 0, 100% 15%, 100% 100%, 0 100%);
    position: relative;
}

.top-angle-left:after {
    -webkit-clip-path: polygon(0 0, 100% 15%, 100% 100%, 0 85%);
    clip-path: polygon(0 0, 100% 15%, 100% 100%, 0 85%);
    background: #e99d15;
    content: '';
    position: absolute;
    left: 0;
    top: -82%;
    right: 0;
    height: 100%;
    display: block;
    z-index: 9;
}

The idea that the :after element can ALWAYS scale with the its parent container, so now this is 100% responsive. This is only accomplished by applying the negative top to the :after element. Hope this is helpful!!

kiner_shah
  • 3,939
  • 7
  • 23
  • 37
0

I have a cleaner solution than many of the css solutions here, however it breaks down if you're using a shape that is concave or has corners with very small angles. In my example, the left side of the shape shows how it looks alright for most convex shapes and the right side shows the problems you'd run into with concave ones. Still, it's easy and cleaner than having to change positioning and moving elements around.

:root {
  --indent: 15%;
  --arrow-shape: polygon(
    var(--indent) 0,
    0 50%,
    var(--indent) 100%,
    100% 100%,
    calc(100% - var(--indent)) 50%,
    100% 0
  );
}

.parallelogram-outline {
  display: inline-block;
  background-color: black;
  padding: 5px;
  clip-path: var(--arrow-shape);
}


.parallelogram {
  font-family: sans-serif;
  background-color: white;
  padding: 0.25rem 0.75rem 0.25rem 0.5rem;
  clip-path: var(--arrow-shape);
}
<div class="parallelogram-outline">
  <div class="parallelogram">
    Text
  </div>
</div>
dinx
  • 105
  • 4