CSS custom shape with irregular rectangle and border - css

I'm trying to create a button like this:
After researching online, I only came up with making a parallelogram. But this is the result:
Code:
.parallelogram {
width: 100px;
height: 50px;
transform: skew(25deg);
background: black;
border: 1px solid #EC00F4;
color: white;
box-shadow: 0px 3px 8px #EC00F4;
}
<button class="parallelogram"> Hello button </button>
Is there a way to make the edges go where I want (like in the picture) but without moving the text ?

Use clip-path on pseudo elements. The trick is to consider the same clip-path and apply a scale transformation to one pseudo element to simulate the border. Simply adjust the value of the polygon to get the needed result.
Hover to see a different clip-path:
.parallelogram {
padding:20px 45px;
font-size:30px;
color: white;
border:none;
background:none;
outline:none;
position:relative;
z-index:0;
margin:15px;
filter:drop-shadow(0px 30px 25px rgba(236, 0, 244, 0.45));
}
.parallelogram:before,
.parallelogram:after {
content:"";
position:absolute;
z-index:-1;
top:0;
left:0;
right:0;
bottom:0;
clip-path: polygon(0 11%, 100% 0, 90% 88%, 3% 96%);
transition:1s all;
background:#000;
}
.parallelogram:before {
background:#EC00F4;
transform:scale(1.05,1.12);
}
.parallelogram:hover:before,
.parallelogram:hover:after {
clip-path: polygon(5% 2%, 100% 5%, 100% 100%, 0% 94%);
}
<button class="parallelogram"> Hello button </button>
<button class="parallelogram"> button </button>
<button class="parallelogram"> A </button>
You can also consider pixel value to keep the same shape whataver the content inside:
.parallelogram {
padding:20px 45px;
font-size:30px;
color: white;
border:none;
background:none;
outline:none;
position:relative;
z-index:0;
margin:15px;
filter:drop-shadow(0px 30px 25px rgba(236, 0, 244, 0.45));
}
.parallelogram:before,
.parallelogram:after {
content:"";
position:absolute;
z-index:-1;
top:0;
left:0;
right:0;
bottom:0;
clip-path: polygon(0 10px, 100% 0, calc(100% - 8px) calc(100% - 15px), 5px calc(100% - 8px));
transition:1s all;
background:#000;
}
.parallelogram:before {
background:#EC00F4;
transform:scale(1.05,1.12);
}
.parallelogram:hover:before,
.parallelogram:hover:after {
clip-path: polygon(0 5px, 100% 0, 100% 100%, 10px calc(100% - 20px));
}
<button class="parallelogram"> Hello button </button>
<button class="parallelogram"> button </button>
<button class="parallelogram"> A </button>

This works kinda like you want it:
button{
width: 150px;
height: 50px;
-webkit-clip-path: polygon(0% 20%, 92% 14%, 88% 88%, 0% 100%);
clip-path: polygon(0% 20%, 92% 14%, 88% 88%, 0% 100%);
background: black;
color: white;
}
<button class="parallelogram"> Hello button </button>
EDIT:
You can create an SVG that looks exactly like your button here: https://vectr.com/new
You can add border + shadow and simply copy the html.

You could use a pseudo element to set your perspective effect:
example
.parallelogram {
width: 100px;
height: 50px;
position: relative;
color: white;
/* appearance:none; could be used too */
background: none;
border: none;
cursor: pointer; /* show where and that i'm clickable */
}
.parallelogram:before {
content: "";
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 110%; /* might need to be resized */
height: 100%;
transform: /* tune the rotation and perspective values to your needs */
perspective(200px)
rotatey(35deg)
rotatex(-25deg)
;
background: black;
border: 2px solid #ec00f4;
box-shadow: 0px 3px 8px #ec00f4;
}
<button class="parallelogram"> Hello button </button>
screenshot from firefox :

Add a span, with the class .unskew and do the opposite of your skew effect on the background and change the display rule.
Example:
CSS
.parallelogram {
transition: background 0.3s;
transform: skew(-25deg);
/* SKEW */
}
.unskew{
display: block;
/* block or inline-block is needed */
text-decoration: none;
padding: 5px 10px;
font: 30px/1 sans-serif;
transform: skew(25deg);
/* UNSKEW */
color: inherit;
}
HTML
<button class="parallelogram"><span class="unskew" >Hello button</span></button>

Related

button with gradient background and rounded outline

Currently I am using image of the whitish border and inside that I am using the button. But it has responsive issues. Can we create the whole thing with css or eliminate the responsive issue if it cant be done with css.
.header-btn-section img{
display: block;
margin: 0 auto;
margin-top: -23px;
width: 370px;
height: 80px;
}
.header-btn {
padding: 15px 40px 15px 40px;
background: #5760f4;
background-image: -webkit-linear-gradient(left, #5760f4 , #f3135d);
background-image: linear-gradient(to right, #5760f4 , #f3135d);
border-radius: 40px;
border: none;
text-transform: uppercase;
color: #fff;
font-size: 25px;
font-weight: bold;
}
.header-btn:hover {
background: #6e73df;
background-image: -webkit-linear-gradient(left, #f3135d, #5760f4);
background-image: linear-gradient(to right,#f3135d,#5760f4);
}
.btn-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<div class="header-btn-section" style="position: relative">
<img class="btn-background" src="https://i.imgur.com/StNBlDd.png">
<div class="btn-container">
<button class="header-btn">Try DddxdVdDk Free !</button>
</div>
</div>
You can do this with one element considering background, border and background-clip:
.box {
width:200px;
height:70px;
border-radius:70px;
padding:5px; /* Control the space between border and background*/
background-image:linear-gradient(to right,red, blue);
background-clip:content-box; /* Don't color the padding */
border:3px solid #fff;
color:#fff;
font-size:20px;
}
.box:hover {
background-image:linear-gradient(to left,red, blue);
}
body {
background:pink;
}
<button class="box">Some text here</button>
If you want to use the padding to control the spacing, use pseudo element:
.box {
padding:20px 40px;
max-width:220px;
border-radius:70px;
position:relative;
z-index:0;
border:none;
background:none;
color:#fff;
font-size:20px;
}
.box:before {
content:"";
position:absolute;
z-index:-1;
top:0;
left:0;
right:0;
bottom:0;
border-radius:inherit;
padding:5px; /* Control the space between border and background*/
background-image:linear-gradient(to right,red, blue);
background-clip:content-box; /* Don't color the padding */
border:3px solid #fff;
}
.box:hover::before {
background-image:linear-gradient(to left,red, blue);
}
body {
background:pink;
}
<button class="box">Some text here</button>
<button class="box">Some long text here</button>

How to achieve "depth" with a 3d css transform

I am trying to create a "perspective mockup" using CSS. There are a fair amount of tutorials on how to achieve this with 3D layers in Photoshop, but I would like to do it with CSS. Here is an example of what I am trying to achieve:
And here is the code (using the raw image, https://i.imgur.com/foDEYpB.png):
#perspective {
width: 400px;
height: 500px;
position: absolute;
background-image: url("https://i.imgur.com/foDEYpB.png");
background-repeat: no-repeat;
background-size: cover;
top: 50%;
left: 50%;
margin-left: -200px;
margin-top: -250px;
transform: rotate3d(360, 120, -90, 60deg) rotateZ(-30deg);
box-shadow: -15px 15px 20px rgba(0, 0, 0, 0.5);
}
<div id='perspective'></div>
I am pretty close, but am unsure how to achieve the "depth" or "height" where the image looks raised. Zoomed in version of said "depth" where the image is repeated onto the sides:
P.S. if anyone knows the correct name for what I'm referring to as "depth", I'd love to know!
Try adding three type of images to make 3D effects. Use transform property with rotation for images to get the desired result.
Answer reference here.
.perspective {
position: relative;
width: 400px;
height: 500px;
transform-style: preserve-3d;
transition: all 500ms ease-in;
transform: rotateY(20deg) rotateX(60deg) rotateZ(-10deg);
transform: rotateY(15deg) rotateX(50deg) rotateZ(-15deg);
box-shadow: -40px 80px 80px -10px rgba(0, 0, 0, 0.7);
cursor: pointer;
margin-right: 30px;
display: inline-block;
margin-left: 30%;
}
.perspective img {
position: absolute;
top: 0px;
left: 0px;
width: 400px;
height: 500px;
transform: translateZ(16px);
}
.bottom,
.left {
position: absolute;
width: 400px;
height: 500px;
display: block;
transition: all 1s linear;
overflow: hidden;
border-radius: 3px;
transform: translateZ(16px);
filter: brightness(80%)
}
.left {
transform: rotateY(270deg) translateX(-1px);
transform-origin: center left;
width: 18px;
}
.bottom {
transform: rotateX(90deg) translateY(15px) translateZ(-480px);
transform-origin: bottom center;
height: 18px;
}
.bottom img {
transform: rotateX(180deg);
width: 100%;
height: 500px;
left: 0px;
}
<div class="perspective">
<img src="https://i.imgur.com/foDEYpB.png">
<div class="bottom"><img src="https://i.imgur.com/foDEYpB.png"></div>
<div class="left"><img src="https://i.imgur.com/foDEYpB.png"></div>
</div>
Here is a hacky idea using multiple background to simulate such effect. The trick is to add 2 semi-transparent gradients to create the shadow effect then 2 other gradient to cut a small part of the corner to obtain the 3D shape.
The result may not be perfect for all the images:
.wrapper {
display:inline-block;
perspective:1000px;
}
.box {
margin: 50px;
width:200px;
height:200px;
transform: rotate3d(360, 120, -90, 60deg) rotateZ(-30deg);
background:
linear-gradient(to bottom right,transparent 49%,#fff 52%) bottom right/14px 10px,
linear-gradient(to top left,transparent 49%,#fff 52%) top left /10px 14px,
linear-gradient(rgba(0,0,0,0.5),rgba(0,0,0,0.5)) 0 0px/10px 100%,
linear-gradient(rgba(0,0,0,0.5),rgba(0,0,0,0.5)) 100% 100%/calc(100% - 10px) 10px,
url(https://picsum.photos/id/1061/1000/800) center/cover;
background-repeat:no-repeat;
}
<div class="wrapper" >
<div class="box" >
</div>
</div>
With your image you can have a specific gradient like below:
body {
background:#ccc;
}
.wrapper {
display:inline-block;
perspective:1000px;
}
.box {
margin: 50px;
width:200px;
height:250px;
transform: rotate3d(360, 120, -90, 60deg) rotateZ(-30deg);
background:
linear-gradient(to bottom right,transparent 49%,#ccc 52%) bottom right/16px 10px,
linear-gradient(to top left,transparent 49%,#ccc 52%) top left /10px 12px,
linear-gradient(#efefef,#efefef) 100% 100%/calc(100% - 10px) 10px,
linear-gradient(-226deg,#222428 13px,#ff4946 13px,#ff4946 77px,#592D30 77px,#592D30 100px,#222428 100px,#222428 108px,#efefef 108px,#efefef 161px) 0 0px/10px 100%,
url(https://i.imgur.com/foDEYpB.png) center/cover;
background-repeat:no-repeat;
}
<div class="wrapper">
<div class="box">
</div>
</div>

How to make a triangle hover effect behind menu option

I'm trying to add a hover effect for a menu -
It should be pretty simple but I haven't found any scss or css work arounds yet... Below is an image that shows specifically what I'm talking about.
A simple linear-gradient will do it:
.container {
background:grey;
padding:10px;
}
.nav {
display: inline-block;
padding: 10px;
color: #fff;
}
.nav:hover {
background: linear-gradient(to bottom right, transparent 50%, red 51%);
}
<div class="container">
<div class="nav">TEXT</div>
<div class="nav">long TEXT</div>
<div class="nav">A</div>
<div class="nav">BBBBBBBBBBB</div>
</div>
You can use clip-path to shape the triangle, although browser support Eh.
button {
background: #112b1bb8;
border: 1px solid black;
padding: 10px 40px;
position: relative;
}
button:hover:before {
content: '';
width: 100%;
height: 100%;
background: grey;
position: absolute;
top: 0;
left: 0;
z-index: -1;
clip-path: polygon(0% 100%, 100% 0%, 100% 100%);
}
<button>Brand</button>
<button>Link Link</button>
<button>O</button>

How to create gradient color

I need to create the background color as like an below image.. how to acheive this background color using gradient?
Note: just background color for that pic no need for needle, tick and label.
You can get it by using radial-gradient.
Below i posted a working example to get it, You can play with radial-gradient properties to understand how it's work.
Working fiddle
Radial gradient
.round {
width:300px;
height:300px;
border-radius:50%;
background: rgba(0,0,0,0.8);
background-image:radial-gradient(circle at 50% 30%,#cacaca,#333);
-webkit-background-image:radial-gradient(circle at 50% 30%,#cacaca,#333);
position:relative;
overflow:hidden;
}
.round:after, .round:before {
content:'';
width:100%;
height:100%;
position:absolute;
border-radius:50%;
}
.round:before {
left:0;
top:30px;
transform: rotate(22deg);
transform-origin: -13% 52%;
-webkit-transform-origin: -13% 52%;
background-image: radial-gradient(circle at -11% 30%,#333,#999);
background-image: -webkit-radial-gradient(circle at -11% 30%,#333,#999);
opacity:0.2;
}
.round:after {
right:0;
top:30px;
transform: rotate(22deg);
transform-origin: 22% 125%;
-webkit-transform-origin: 22% 125%;
background-image: radial-gradient(circle at -45% 30%,#999,#333);
-webkit-background-image: radial-gradient(circle at -45% 30%,#999,#333);
opacity:0.2;
}
<div class="round"></div>
Close eh?
Let me know if you need fuller CSS breakdown but personally, I've never used radial gradients in CSS before but it seems to work well.
Here's what I used as a reference.
.container {
position: relative;
width: 300px;
height: 300px;
}
.radial-gradient {
position: absolute;
width: 100%;
height: 100%;
border-style: soild;
border-width: 5px;
border-radius: 50%;
background-image: radial-gradient(circle at 50% 30% , #C3C3C3 0%, #000000 100%);
}
<div class="container">
<div class="radial-gradient">
</div>
</div>
I am able to produce a similar effect using either a radial gradient or the box-shadow property.
Note 1: gradients are rendered differently across different browsers.
Note 2: too much blur in the box-shadow property is bad for performance
Examples (might need some fine-tuning on your end):
.circle {
height: 250px;
width: 250px;
border-radius: 100vw;
background: white;
margin: 1em auto;
}
.gradient {
background-position: -55px -86px;
background-image: radial-gradient(circle, rgb(249, 249, 249) -4%, #000000 81%);
background-repeat: no-repeat;
background-size: 136%;
}
.box-shadow {
box-shadow: inset -7px -28px 140px 48px rgba(0, 0, 0, 0.75);
}
.sample {
text-align: center;
border: 1px solid #444;
width: 300px;
margin: 1em;
}
<div class="sample">Gradient
<div class="circle gradient"></div>
</div>
<div class="sample">Box-shadow
<div class="circle box-shadow"></div>
</div>
Recommendation? Use a an image instead of CSS in this case.

How to access WordPress database from Plugins file [duplicate]

I have a project where I need to insert speech bubbles / message boxes. The general shape I am trying to achieve is this one :
.bubble {
height: 100px;
width: 200px;
border: 3px solid gray;
background: lightgray;
position: relative;
cursor:pointer;
}
.triangle {
width: 0;
border-top: 20px solid black;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
cursor:pointer;
}
<div class="bubble">Speech bubble
</div>
<div class="triangle">
</div>
This currently does not pass a hit-test as the transparent border is also clickable.
Objectives
The hit box (clickable / hoverable areas) needs to stick to the shape's boundaries (the transparent borders here are also hoverable, invalidating this).
I need to display the shape over various content (images, gradents, text...),
Issues
The main issues I am having when manipulating this shape are:
Have the ability to move the triangle around the speech bubble according to the position of the element it refers to (top/left/right/bottom sides)
adding a border or box shadow around it when emphasis is needed
Is there anyway of addressing these issues?
In order to achieve this, you should consider altering your markup in order to make your html more efficient. This can be achieved using a pseudo element. I'll address each point individually, and put it all together at the end of my answer.
First of all,
Use pseudo elements to avoid extra elements
You could use a pseudo element to remove the extra .triangle div. This not only reduces your div numbers, but also helps with positioning as you can use the top: left: right: and bottom: css properties in order to position according to your main element. This can be seen below:
.oneAndOnlyDiv {
height: 100px;
width: 200px;
border: 3px solid gray;
background: lightgray;
position: relative;
}
.oneAndOnlyDiv:before {
content: "";
position: absolute;
top: 100%;
left: 20px;
width: 0;
border-top: 20px solid black;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}
<div class="oneAndOnlyDiv">Main div</div>
Hit testing
In order to create your "hit test", you may wish to use a rotated element instead of a border hack.
Something like:
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
}
div:before {
content: "";
position: absolute;
top: 100%;
left: 20px;
height: 20px;
width: 20px;
background: black;
transform: rotate(45deg);
transform-origin:top right;
}
<div>Only element</div>
or use a skewed pseudo element:
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
}
div:before {
content: "";
position: absolute;
top: 90%;
left: 20px;
height: 30%;
width: 20px;
background: black;
transform: skewY(-45deg);
transform-origin:bottom left;
z-index:-1;
}
<div>Only element</div>
which will show the pointer only when the square or main element is hovered.
But hang on, that messes up the positioning? how can you deal with that?
There are a few solutions to that. One of which is to use the calc CSS property.
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
}
div:before {
content: "";
position: absolute;
top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
top: calc(100% - 10px); /*i.e. half the height*/
left: 20px;
height: 20px;
width: 20px;
background: gray;
transform: rotate(45deg);
}
<div>Only element</div>
Adding a border
You can add a border quite easily now, simply by adding a border declaration to the main element, and setting the border-bottom and border-right of the pseudo element to inherit
Border
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
border:3px double black;
}
div:before {
content: "";
position: absolute;
top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
top: calc(100% - 10px); /*i.e. half the height*/
left: 20px;
height: 20px;
width: 20px;
background: gray;
transform: rotate(45deg);
border-bottom:inherit;
border-right:inherit;
box-shadow:inherit;
}
<div>Only element</div>
Box Shadow:
In order to have a box shadow, I've used the :after pseudo element in order to hide the box shadow over the other pseudo, making the element seem as one single element.
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
box-shadow: 5px 5px 10px 2px black;
}
div:before,div:after {
content: "";
position: absolute;
top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
top: calc(100% - 10px); /*i.e. half the height*/
left: 20px;
height: 20px;
width: 20px;
background: gray;
transform: rotate(45deg);
z-index:-1;
box-shadow:inherit;
}
div:after{
box-shadow:none;
z-index:8;
}
<div>Only element</div>
Putting it all together
You can also add a border radius to your message box or speech bubble by again, using the border-radius property:
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
border:3px double black;
border-radius:10px;
}
div:before {
content: "";
position: absolute;
top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
top: calc(100% - 10px); /*i.e. half the height*/
left: 20px;
height: 20px;
width: 20px;
background: gray;
transform: rotate(45deg);
border-bottom:inherit;
border-right:inherit;
box-shadow:inherit;
}
<div>Only element</div>
This even allows you to create not only a triangle, but how about a circle instead?
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor:pointer;
border:3px double black;
border-radius:10px;
}
div:before {
content: "";
position: absolute;
top: -webkit-calc(100% - 13px); /*may require prefix for old browser support*/
top: calc(100% - 13px); /*i.e. half the height + border*/
left: 20px;
height: 20px;
width: 20px;
background: gray;
transform: rotate(45deg);
border:3px double transparent;
border-bottom:inherit;
border-right:inherit;
box-shadow:inherit;
border-radius:50%;
}
<div>Only element</div>
If you are having issues with content overflowing and being 'hidden' behind this pseudo element, and you aren't fussed about having a border, you could use a negative z-index which will solve this issue.
Don't like using 'magic numbers'?
If you don't like the idea of using a calc value, in which the positioning in my answer is currently using (whilst working), you may wish to use transform:translate(50%)
This would be a much better approach, since:
You do not need to know the size of the border, nor half the width
You will be making your message box/ bubble a lot more dynamic in its positioning, and would support further sizings.
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor: pointer;
border: 3px double black;
border-radius: 10px;
}
div:before {
content: "";
position: absolute;
top: 100%;
left: 30px;
height: 20px;
width: 20px;
background: gray;
box-sizing:border-box;
transform: rotate(45deg) translate(-50%);
border-bottom: inherit;
border-right: inherit;
box-shadow: inherit;
}
<div>Only element</div>
Want to move it? You can!
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor: pointer;
border: 3px double black;
border-radius: 10px;
}
div:before {
content: "";
position: absolute;
top: 100%;
left: 10%;
height: 20px;
width: 20px;
background: gray;
box-sizing: border-box;
transform: rotate(45deg) translate(-50%);
border-bottom: inherit;
border-right: inherit;
box-shadow: inherit;
transition: all 0.8s;
}
div:hover:before {
left: 90%;
}
<div>Only element</div>
Want it one the right?
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor: pointer;
border: 3px double black;
border-radius: 10px;
}
div:before {
content: "";
position: absolute;
top: 15%;
left: 100%;
height: 20px;
width: 20px;
background: gray;
box-sizing:border-box;
transform: rotate(45deg) translate(-50%);
border-top: inherit;
border-right: inherit;
box-shadow: inherit;
transition:all 0.8s;
}
div:hover:before{
top:80%;
}
<div>Only Element</div>
Want it to be a different shape of triangle?
div {
height: 100px;
width: 200px;
background: gray;
position: relative;
cursor: pointer;
border-radius: 10px;
}
div:before {
content: "";
position: absolute;
top: 70%;
left: 100%;
height: 20px;
width: 20px;
background: gray;
box-sizing:border-box;
transform: translate(-50%) skewX(45deg);
box-shadow: inherit;
transition:all 0.8s;
z-index:-1;
}
div:hover:before{
transform: translate(-50%);
border-radius:50%;
top:20%;
}
<div>Only Element</div>
We can rely on clip-path and drop-shadow filter to easily achieve this:
.box {
margin: 50px;
width: 200px;
height: 100px;
border-radius: 15px;
background: red;
position: relative;
filter: /* the more shadow you add the thicker the border will be */
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green);
}
.box::before {
content: "";
position: absolute;
top: 100%;
left: 20%;
height: 30px;
width: 50px;
background: inherit;
clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.box:hover {
background:blue;
}
body {
background:linear-gradient(to right, pink,grey);
}
<div class="box"></div>
We can extend this basic example to consider any kind of position and triangle shape:
.box {
margin: 30px;
width: 150px;
height: 80px;
display:inline-block;
border-radius: 15px;
background: red;
position: relative;
filter: /* the more shadow you add the thicker the border will be */
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green);
}
.box::before {
content: "";
position: absolute;
height: var(--h,20px);
width: var(--w,30px);
background: inherit;
transform:scale(var(--x,1),var(--y,1));
}
.box.p-bottom::before{
top: 100%;
clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.box.p-bottom.alt::before{
clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.box.p-top::before{
bottom: 100%;
clip-path: polygon(0 100%, 100% 100%, 50% 0);
}
.box.p-top.alt::before{
clip-path: polygon(0 100%, 100% 100%, 100% 0);
}
.box.p-left::before{
right: 100%;
clip-path: polygon(100% 0, 100% 100%,0% 50%);
}
.box.p-left.alt::before{
clip-path: polygon(100% 0, 100% 100%,0% 100%);
}
.box.p-right::before{
left: 100%;
clip-path: polygon(0% 0, 0% 100%,100% 50%);
}
.box.p-right.alt::before{
clip-path: polygon(0% 0, 0% 100%,100% 100%);
}
.box.right::before{
right:var(--p,20px);
}
.box.left::before {
left:var(--p,20px);
}
.box.top::before{
top:var(--p,20px);
}
.box.bottom::before {
bottom:var(--p,20px);
}
.box:hover {
background:blue;
}
body {
background:linear-gradient(to right, pink,grey);
}
<div class="box p-bottom right"></div>
<div class="box p-bottom right alt"></div>
<div class="box p-bottom right alt" style="--x:-1"></div>
<div class="box p-top left"></div>
<div class="box p-top right" style="--p:40%"></div>
<div class="box p-top right alt" style="--p:40%"></div>
<div class="box p-left top"></div>
<div class="box p-left top alt"></div>
<div class="box p-right bottom" style="--w:20px;"></div>
<div class="box p-right bottom" style="--p:30px;--w:20px;--h:30px"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--y:-1"></div>
We can also consider any kind of background for the whole shape. The trick work for a fixed width/height. The idea is to create a background having the same size for both the main and pseudo element then we simply adjust the position of the one inside the pseudo element to match the one of the parent (to have a perfect overlap)
.box {
--h:20px;
--w:30px;
--p:20px;
margin: 30px;
width: 150px;
height: 80px;
display:inline-block;
border-radius: 15px;
background:
var(--back,linear-gradient(45deg,red,purple))
center/
calc(150px + 2*var(--w)) calc(80px + 2*var(--h));
position: relative;
filter: /* the more shadow you add the thicker the border will be */
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green)
drop-shadow(0px 0px 1px green);
}
.box::before {
content: "";
position: absolute;
height: var(--h);
width: var(--w);
background: inherit;
transform:scale(var(--x,1),var(--y,1));
background-position:var(--b1) 0 var(--b2);
}
.box.p-bottom::before{
top: 100%;
clip-path: polygon(0 0, 100% 0, 50% 100%);
--b1:bottom;
}
.box.p-bottom.alt::before{
clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.box.p-top::before{
bottom: 100%;
clip-path: polygon(0 100%, 100% 100%, 50% 0);
--b1:top;
}
.box.p-top.alt::before{
clip-path: polygon(0 100%, 100% 100%, 100% 0);
}
.box.p-left::before{
right: 100%;
clip-path: polygon(100% 0, 100% 100%,0% 50%);
--b1:left;
}
.box.p-left.alt::before{
clip-path: polygon(100% 0, 100% 100%,0% 100%);
}
.box.p-right::before{
left: 100%;
clip-path: polygon(0% 0, 0% 100%,100% 50%);
--b1:right;
}
.box.p-right.alt::before{
clip-path: polygon(0% 0, 0% 100%,100% 100%);
}
.box.right::before{
right:var(--p);
--b2:right calc(-1*var(--p) - var(--w));
}
.box.left::before {
left:var(--p);
--b2:left calc(-1*var(--p) - var(--w));
}
.box.top::before{
top:var(--p);
--b2:top calc(-1*var(--p) - var(--h));
}
.box.bottom::before {
bottom:var(--p);
--b2:bottom calc(-1*var(--p) - var(--h));
}
body {
background:linear-gradient(to right, pink,grey);
}
<div class="box p-bottom right"></div>
<div class="box p-bottom right alt" style="--back:url(https://picsum.photos/id/15/400/300)"></div>
<div class="box p-bottom right alt" style="--x:-1;--back:red"></div>
<div class="box p-top left" style="--back:url(https://picsum.photos/id/18/400/300)"></div>
<div class="box p-top right" style="--p:40px;--back:url(https://picsum.photos/id/1018/400/300)"></div>
<div class="box p-top right alt" style="--p:60px;--back:radial-gradient(red,pink,yellow)"></div>
<div class="box p-left top" style="--back:black"></div>
<div class="box p-left top alt" style="--back:repeating-linear-gradient(45deg,#fff 0 10px,orange 0 20px)"></div>
<div class="box p-right bottom" style="--w:20px;--back:linear-gradient(red,pink,yellow)"></div>
<div class="box p-right bottom" style="--p:30px;--w:20px;--h:30px;--back:repeating-radial-gradient(#fff 0 10px,orange 0 20px)"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--back:conic-gradient(red,pink,yellow,red)"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--y:-1;"></div>
SVG
This does not pass a hit-test as the transparent border is also clickable
This can be done using the pointer-events in svg.
pointer-events:visibleFill; Will only select the part where there is paint.
This example uses filter_box-shadow and is not supported by IE.
Also uses two shapes.
html,
body {
margin: 0;
padding: 0;
}
.bubble {
width: 150px;
height: 150px;
-webkit-filter: drop-shadow(5px 5px 0px #aaa);
filter: drop-shadow(5px 5px 0px #aaa);
}
.bubble-shape {
fill: #1e1;
}
.shape-text {
color: black;
}
<svg class="bubble" viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<g class="bubble-shape" style="cursor:pointer; pointer-events:visibleFill;">
<rect x="10" y="10" width="90" height="90" rx="15" ry="15" />
<polygon points="20,94 40,94 30,105" />
</g>
</svg>
This example uses one path
Should be fully supported by IE.
html,
body {
margin: 0;
padding: 0;
}
.bubble {
width: 150px;
height: 150px;
}
.bubble-shape {
stroke-width: 15;
stroke: #ddd;
fill: #1e1;
}
.shape-text {
color: black;
}
<svg class="bubble" viewBox="-70 -10 390 370" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<g style="cursor:pointer; pointer-events:visible;">
<path class="bubble-shape" d="m 0,0 250,0 c 25,0 50,20 50,50 l 0,225 c 0,25 -25,50 -50,50 l -175,0 -25,20 -20,-20 -40,0 c -25,0 -50,-25 -50,-50 l 0,-225 C -50,25 -50,0 0,0 Z" />
</g>
</svg>

Resources