How to create rounded borders with gap? - css

I'm trying to create blocks and elements with rounded corners and a gap between the corner and the straight line. Turns out its a little bit difficult :)
I also have to create "button" like this,
and
If I can not create them in pure CSS I will use svg for this, but I have to solve the above problem first.
Anyone have some idea?

Looks like a job for a border-image.
const grab = document.getElementById('grab');
console.log("url('data:image/svg+xml;base64,"+btoa(grab.innerHTML.replace(/\r?\n */g,''))+"')");
.border-gap {
border: 16px solid;
border-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHBhdGggc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIGQ9Ik0xLDEzIEExMiwxMiAwIDAgMSAxMywxTTE5LDEgSDI5TTM1LDEgQTEyLDEyIDAgMCAxIDQ3LDEzTTQ3LDE5IFYyOU00NywzNSBBMTIsMTIgMCAwIDEgMzUsNDdNMjksNDcgSDE5TTEzLDQ3IEExMiwxMiAwIDAgMSAxLDM1TTEsMjkgVjE5Ij48L3BhdGg+PC9zdmc+') calc(100% * 19 / 48);
padding: 8px;
}
<div class="border-gap">Text</div>
<template id="grab">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path stroke="black" stroke-width="2" stroke-linecap="round" fill="none" d="
M1,13 A12,12 0 0 1 13,1
M19,1 H29
M35,1 A12,12 0 0 1 47,13
M47,19 V29
M47,35 A12,12 0 0 1 35,47
M29,47 H19
M13,47 A12,12 0 0 1 1,35
M1,29 V19
" />
</svg>
</template>
I've included the SVG I used as well as the method for getting a data: URL for it, which can then be put in the border-image.

mask can help you:
.box {
--r:40px; /* radius*/
--g:5px; /* gap */
width:200px;
height:100px;
display:inline-block;
border:5px solid red;
box-sizing:border-box;
margin:10px;
border-radius:var(--r);
--m:#0000 90deg,#000 0;
-webkit-mask:
conic-gradient(from -180deg at calc(100% - var(--g)) var(--g) ,var(--m))
0 100%/var(--r) var(--r),
conic-gradient(from 90deg at var(--g) var(--g) ,var(--m))
100% 100%/var(--r) var(--r),
conic-gradient(from 0deg at var(--g) calc(100% - var(--g)),var(--m))
100% 0 /var(--r) var(--r),
conic-gradient(from -90deg at calc(100% - var(--g)) calc(100% - var(--g)),var(--m))
0 0 /var(--r) var(--r),
linear-gradient(#000 0 0);
-webkit-mask-repeat:no-repeat;
-webkit-mask-composite:xor;
mask-composite:exclude;
}
<div class="box"></div>
<div class="box" style="width:100px;--r:50px"></div>
<div class="box" style="width:150px;--r:50px"></div>
To have content, use it as pseudo element:
.box {
--r:30px; /* radius*/
--g:5px; /* gap */
width:200px;
height:100px;
display:inline-block;
box-sizing:border-box;
padding:20px;
position:relative;
margin:10px;
}
.box:before {
content:"";
position:absolute;
inset:0;
border:5px solid red;
border-radius:var(--r);
--m:#0000 90deg,#000 0;
-webkit-mask:
conic-gradient(from -180deg at calc(100% - var(--g)) var(--g) ,var(--m))
0 100%/var(--r) var(--r),
conic-gradient(from 90deg at var(--g) var(--g) ,var(--m))
100% 100%/var(--r) var(--r),
conic-gradient(from 0deg at var(--g) calc(100% - var(--g)),var(--m))
100% 0 /var(--r) var(--r),
conic-gradient(from -90deg at calc(100% - var(--g)) calc(100% - var(--g)),var(--m))
0 0 /var(--r) var(--r),
linear-gradient(#000 0 0);
-webkit-mask-repeat:no-repeat;
-webkit-mask-composite:xor;
mask-composite:exclude;
}
<div class="box"> content </div>
<div class="box" style="width:100px;--r:50px">content </div>
<div class="box" style="width:150px;--r:50px">content </div>

The hard way...
body {
background: #232323;
}
.btn {
border: 3px solid #fafafa;
padding: 2px 12px;
border-radius: 50%;
background: transparent;
position: relative;
}
.btn::before,
.btn::after {
position: absolute;
content: '';
width: 3px;
height: 5px;
background: #232323;
top: 40%
}
.btn::before {
left: -10%;
}
.btn::after {
right: -8%;
}
.btn:hover {
border-color: #ff3844
}
.btn:hover .icon {
color: #ff3844
}
.icon {
color: #fafafa;
font-size: 1.5rem;
font-weight: bold
}
span {
position: relative
}
span::before,
span::after {
position: absolute;
content: '';
width: 5px;
height: 4px;
background: #232323;
left: -2px;
}
span::before {
top: -22px
}
span::after {
bottom: -2px
}
<button class="btn">
<div class='icon'>+</div>
<span></span>
</button>

Related

Border image bug in Safari / on iOS [duplicate]

I have the following CSS:
a.btn.white-grad {
background: $lgrey;
color: #313149 !important;
border: 1px solid #000;
border-image-source: linear-gradient(to right, #9c20aa, #fb3570);
border-image-slice: 20;
float: left;
#include font-size(26);
margin: 75px 0;
}
Adding border-radius: 5px doesn't seem to do anything. I figured it's because I'm using a border gradient... is there a way for me to achieve the desired 5px border radius at all?
2021: I recommend using the CSS mask method since the support is pretty good now
You cannot use border-radius with gradient. Here is another idea where you can rely on multiple background and adjust the background-clip:
.white-grad {
background:
linear-gradient(#ccc 0 0) padding-box, /*this is your grey background*/
linear-gradient(to right, #9c20aa, #fb3570) border-box;
color: #313149;
padding: 10px;
border: 5px solid transparent;
border-radius: 15px;
display: inline-block;
margin: 75px 0;
}
<div class="white-grad"> Some text here</div>
<div class="white-grad"> Some long long long text here</div>
<div class="white-grad"> Some long long <br>long text here</div>
SVG method
If you want transparency, you can consider SVG like below:
svg {
width:200px;
height:100px;
margin:10px;
}
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#9c20aa" offset="0"/>
<stop stop-color="#fb3570" offset="1"/>
</linearGradient>
</defs>
<rect x="5" y="5" height="100%" width="100%" style="width:calc(100% - 10px);height:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(#Gradient)"/>
</svg>
That you can apply as background:
.white-grad {
background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" ><defs><linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop stop-color="%239c20aa" offset="0"/><stop stop-color="%23fb3570" offset="1"/></linearGradient></defs><rect x="5" y="5" width="100%" height="100%" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(%23Gradient)"/></svg>');
color: #313149;
padding:25px;
border-radius:15px;
display:inline-block;
margin: 75px 0;
}
body {
background:yellow;
}
<div class="white-grad"> Some text here</div>
<div class="white-grad"> text very loooooooooooong here</div>
And the same way as mask where you can get the gradient outside of the SVG:
.white-grad {
color: #313149;
padding: 25px;
border-radius: 15px;
display: inline-block;
margin: 75px 0;
background-size: 0 0;
position: relative;
z-index: 0;
}
.white-grad::before {
content: "";
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: inherit;
background-size: auto;
--mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" ><rect x="5" y="5" width="100%" height="100%" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="white"/></svg>');
-webkit-mask: var(--mask);
mask: var(--mask);
}
body {
background: yellow;
}
<div class="white-grad" style="background-image:linear-gradient(to right,blue,red)"> Some text here</div>
<div class="white-grad" style="background-image:linear-gradient(black,lightblue,green)"> text very loooooooooooong here</div>
<div class="white-grad" style="background-image:radial-gradient(blue,pink)"> text very<br> loooooooooooong here</div>
You can also use it as common element and consider position:absolute to place it around the text:
.white-grad {
color: #313149;
padding: 25px;
border-radius: 15px;
display: inline-block;
margin: 75px 0;
position:relative;
z-index:0;
}
.white-grad > svg {
position:absolute;
top:0;
left:0;
height:100%;
width:100%;
z-index:-1;
}
body {
background: yellow;
}
.hide {
height:0;
width:0;
}
<svg class="hide" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="Gradient" x1="0" x2="100" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop stop-color="#9c20aa" offset="0"/><stop stop-color="#fb3570" offset="1"/></linearGradient></defs><rect x="5" y="5" width="100%" height="100%" id="border" style="height:calc(100% - 10px);width:calc(100% - 10px)" rx="20" ry="20" stroke-width="10" fill="transparent" stroke="url(#Gradient)"/></svg>
<div class="white-grad">
<svg xmlns="http://www.w3.org/2000/svg">
<use href="#border" />
</svg>
Some text here</div>
<div class="white-grad">
<svg xmlns="http://www.w3.org/2000/svg">
<use href="#border" />
</svg>
text very loooooooooooong here</div>
CSS Mask method
Here is a different idea with CSS using mask where you will have transparency and it will also be responsive:
.white-grad {
color: #313149;
padding: 10px;
display: inline-block;
margin: 75px 0;
position: relative;
z-index: 0;
}
.white-grad:before {
content: "";
position: absolute;
z-index: -1;
inset: 0;
padding: 5px;
border-radius: 15px;
background: linear-gradient(to right, #9c20aa, #fb3570);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
<div class="white-grad"> Some text here</div>
<div class="white-grad"> Some long long long text here</div>
<div class="white-grad"> Some long long <br>long text here</div>
With CSS variables, we can make it easy to adjust:
.white-grad {
--b:5px; /* border width*/
--r:15px; /* the radius */
color: #313149;
padding: calc(var(--b) + 5px);
display: inline-block;
margin: 75px 0;
position:relative;
z-index:0;
}
.white-grad:before {
content: "";
position: absolute;
z-index: -1;
inset: 0;
padding: var(--b);
border-radius: var(--r);
background: var(--c,linear-gradient(to right, #9c20aa, #fb3570));
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
body {
background:#f2f2f2;
}
<div class="white-grad"> Some text here</div>
<div class="white-grad" style="--r:20px;--b:10px;--c:linear-gradient(140deg,red,yellow,green)"> Some long long long text here</div>
<div class="white-grad" style="--r:30px;--b:8px;--c:linear-gradient(-40deg,black 50%,blue 0)"> Some long long <br>long text here</div>
<div class="white-grad" style="--r:40px;--b:20px;--c:conic-gradient(black,orange,purple)"> Some long long <br>long text here<br> more and more more and more</div>
Related question to get a different effect: How do you apply a gradient from outer to inner, only to borders, in CSS?
The above examples cover also the circle shape:
.white-grad {
--b:5px; /* border width*/
color: #313149;
display: inline-block;
margin: 10px;
width: 150px;
aspect-ratio: 1;
position: relative;
z-index: 0;
}
.white-grad:before {
content:"";
position:absolute;
z-index:-1;
inset: 0;
background: var(--c,linear-gradient(to right, #9c20aa, #fb3570));
padding: var(--b);
border-radius: 50%;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
body {
background:#f2f2f2;
}
<div class="white-grad"></div>
<div class="white-grad" style="--b:10px;--c:linear-gradient(140deg,red,yellow,green)"></div>
<div class="white-grad" style="--b:8px;--c:linear-gradient(-40deg,black 50%,blue 0)"></div>
<div class="white-grad" style="--b:20px;--c:conic-gradient(black,orange,purple)"></div>
Related question in case you want to apply an animation to the border: Button with transparent background and rotating gradient border
Also different radius shapes:
.white-grad {
--b:5px; /* border width*/
color: #313149;
display: inline-block;
margin: 10px;
width: 150px;
aspect-ratio: 1;
position: relative;
z-index: 0;
}
.white-grad:before {
content: "";
position: absolute;
z-index: -1;
inset: 0;
background: var(--c,linear-gradient(to right, #9c20aa, #fb3570));
padding: var(--b);
border-radius: var(--r,50%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
body {
background:#f2f2f2;
}
<div class="white-grad" style="--r:50% 0 50% 50%;"></div>
<div class="white-grad" style="--b:10px;--r:50% 0;--c:linear-gradient(140deg,red,yellow,green)"></div>
<div class="white-grad" style="--b:8px;--r:50% 0 0;--c:linear-gradient(-40deg,black 50%,blue 0)"></div>
<div class="white-grad" style="--b:20px;--r:50% 50% 0 0;--c:conic-gradient(black,orange,purple)"></div>
and different border thickness:
.white-grad {
--b:5px; /* border width*/
color: #313149;
display: inline-block;
margin: 10px;
width: 150px;
aspect-ratio: 1;
position: relative;
z-index: 0;
}
.white-grad:before {
content: "";
position: absolute;
z-index: -1;
inset: 0;
background: var(--c,linear-gradient(#9c20aa, #fb3570));
padding: var(--b);
border-radius:var(--r,50%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
body {
background:#f2f2f2;
}
<div class="white-grad" style="--b:0 0 20px 20px;--r:50% 0 50% 50%;"></div>
<div class="white-grad" style="--b:10px 0 10px 0;--r:50% 0;--c:linear-gradient(140deg,red,yellow,green)"></div>
<div class="white-grad" style="--b:8px 0px 0px 8px;--r:50% 0 0;--c:linear-gradient(40deg,black 50%,blue 0)"></div>
<div class="white-grad" style="--b:20px 20px 0 20px;--r:50% 50% 0 0;--c:conic-gradient(pink,orange,red,pink)"></div>
You need to wrap the button in a div and set the border-radius on that parent div. In order to work, you will have to set overflow:hidden to that parent div as well. Like so:
.btn-wrap {
border-radius: 5px;
overflow: hidden;
margin: 20px;
width: 60px;
}
a.btn.white-grad {
background: #eee;
color: #313149 !important;
border: 20px solid #000;
border-image-source: linear-gradient(to right, #9c20aa, #fb3570);
border-image-slice: 20;
line-height: 2;
}
<div class="btn-wrap">
link
</div>

can we add graduation to an oval form using CSS?

i want to add graduation mark to an oval shape using css, to get the level, let say at 25 % , 50% and 75 %, just a A score mark ('-')
the code i'm using to draw the form is
.circle {
background-color: red;
display: inline-block;
border-width: 10px;
border-radius: 50px;
height: 300px;
width: 200px;
margin-right: 10px;
}
jsfiddle
what i want to get is something like this:
and thanks in advance for everyone
Use a gradient for the main coloration and also for the mark:
.circle {
background:
linear-gradient(#000,#000) 0 25%/50px 5px, /*top */
linear-gradient(#000,#000) 0 50%/100px 5px, /*middle */
linear-gradient(#000,#000) 0 75%/50px 5px, /*bottom*/
/*main color*/
linear-gradient(red,red) bottom/100% 75%;
background-repeat:no-repeat;
display: inline-block;
border-radius: 50px;
height: 200px;
width: 150px;
margin-right: 10px;
border:1px solid;
}
<span class="circle"></span>
And with CSS variable you can make it easy to adjust:
.circle {
background:
linear-gradient(#000,#000) 0 25%/50px 5px,
linear-gradient(#000,#000) 0 50%/100px 5px,
linear-gradient(#000,#000) 0 75%/50px 5px,
/*main color*/
linear-gradient(red,red) bottom/100% var(--p,100%);
background-repeat:no-repeat;
display: inline-block;
border-radius: 50px;
height: 200px;
width: 150px;
margin-right: 10px;
border:1px solid;
}
<span class="circle"></span>
<span class="circle" style="--p:60%"></span>
<span class="circle" style="--p:50%"></span>
<span class="circle" style="--p:30%"></span>

Creating a rectangles with corners removed + stroke in CSS [duplicate]

I'm looking to "cut" the top left corner of a div, like if you had folded the corner of a page down.
I'd like to do it in pure CSS, are there any methods?
If the parent element has a solid color background, you can use pseudo-elements to create the effect:
div {
height: 300px;
background: red;
position: relative;
}
div:before {
content: '';
position: absolute;
top: 0; right: 0;
border-top: 80px solid white;
border-left: 80px solid red;
width: 0;
}
<div></div>
http://jsfiddle.net/2bZAW/
P.S. The upcoming border-corner-shape is exactly what you're looking for. Too bad it might get cut out of the spec, and never make it into any browsers in the wild :(
CSS Clip-Path
Using a clip-path is a new, up and coming alternative. Its starting to get supported more and more and is now becoming well documented. Since it uses SVG to create the shape, it is responsive straight out of the box.
CanIUse
Clip Path Generator
div {
width: 200px;
min-height: 200px;
-webkit-clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 25%, 75% 0);
clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 25%, 75% 0);
background: lightblue;
}
<div>
<p>Some Text</p>
</div>
CSS Transform
I have an alternative to web-tiki's transform answer.
body {
background: lightgreen;
}
div {
width: 200px;
height: 200px;
background: transparent;
position: relative;
overflow: hidden;
}
div.bg {
width: 200%;
height: 200%;
background: lightblue;
position: absolute;
top: 0;
left: -75%;
transform-origin: 50% 50%;
transform: rotate(45deg);
z-index: -1;
}
<div>
<div class="bg"></div>
<p>Some Text</p>
</div>
If you need a transparent cut out edge, you can use a rotated pseudo element as a background for the div and position it to cut out the desired corner:
body {
background: url(http://i.imgur.com/k8BtMvj.jpg);
background-size: cover;
}
div {
position: relative;
width: 50%;
margin: 0 auto;
overflow: hidden;
padding: 20px;
text-align: center;
}
div:after {
content: '';
position: absolute;
width: 1100%; height: 1100%;
top: 20px; right: -500%;
background: rgba(255,255,255,.8);
transform-origin: 54% 0;
transform: rotate(45deg);
z-index: -1;
}
<div>
... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>
</div>
Note that this solution uses transforms and you need to add the required vendor prefixes. For more info see canIuse.
To cut the bottom right edge, you can change the top, transform and transform-origin properties of the pseudo element to:
body {
background: url(http://i.imgur.com/k8BtMvj.jpg);
background-size: cover;
}
div {
position: relative;
width: 50%;
margin: 0 auto;
overflow: hidden;
padding: 20px;
text-align: center;
}
div:after {
content: '';
position: absolute;
width: 1100%; height: 1100%;
bottom: 20px; right: -500%;
background: rgba(255,255,255,.8);
transform-origin: 54% 100%;
transform: rotate(-45deg);
z-index: -1;
}
<div>
... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>... content ...<br/>
</div>
Here is another approach using CSS transform: skew(45deg) to produce the cut corner effect. The shape itself involves three elements (1 real and 2 pseudo-elements) as follows:
The main container div element has overflow: hidden and produces the left border.
The :before pseudo-element which is 20% the height of the parent container and has a skew transform applied to it. This element prodcues the border on the top and cut (slanted) border on the right side.
The :after pseudo-element which is 80% the height of the parent (basically, remaining height) and produces the bottom border, the remaining portion of the right border.
The output produced is responsive, produces a transparent cut at the top and supports transparent backgrounds.
div {
position: relative;
height: 100px;
width: 200px;
border-left: 2px solid beige;
overflow: hidden;
}
div:after,
div:before {
position: absolute;
content: '';
width: calc(100% - 2px);
left: 0px;
z-index: -1;
}
div:before {
height: 20%;
top: 0px;
border: 2px solid beige;
border-width: 2px 3px 0px 0px;
transform: skew(45deg);
transform-origin: right bottom;
}
div:after {
height: calc(80% - 4px);
bottom: 0px;
border: 2px solid beige;
border-width: 0px 2px 2px 0px;
}
.filled:before, .filled:after {
background-color: beige;
}
/* Just for demo */
div {
float: left;
color: beige;
padding: 10px;
transition: all 1s;
margin: 10px;
}
div:hover {
height: 200px;
width: 300px;
}
div.filled{
color: black;
}
body{
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<div class="cut-corner">Some content</div>
<div class="cut-corner filled">Some content</div>
The below is another method to produce the cut corner effect by using linear-gradient background images. A combination of 3 gradient images (given below) is used:
One linear gradient (angled towards bottom left) to produce the cut corner effect. This gradient has a fixed 25px x 25px size.
One linear gradient to provide a solid color to the left of the triangle that causes the cut effect. A gradient is used even though it produces a solid color because we can control size, position of background only when images or gradients are used. This gradient is positioned at -25px on X-axis (basically meaning it would end before the place where the cut is present).
Another gradient similar to the above which again produces a solid color but is positioned at 25px down on the Y-axis (again to leave out the cut area).
The output produced is responsive, produces transparent cut and doesn't require any extra elements (real or pseudo). The drawback is that this approach would work only when the background (fill) is a solid color and it is very difficult to produce borders (but still possible as seen in the snippet).
.cut-corner {
height: 100px;
width: 200px;
background-image: linear-gradient(to bottom left, transparent 50%, beige 50%), linear-gradient(beige, beige), linear-gradient(beige, beige);
background-size: 25px 25px, 100% 100%, 100% 100%;
background-position: 100% 0%, -25px 0%, 100% 25px;
background-repeat: no-repeat;
}
.filled {
background-image: linear-gradient(black, black), linear-gradient(black, black), linear-gradient(black, black), linear-gradient(black, black), linear-gradient(to bottom left, transparent calc(50% - 1px), black calc(50% - 1px), black calc(50% + 1px), beige calc(50% + 1px)), linear-gradient(beige, beige), linear-gradient(beige, beige);
background-size: 2px 100%, 2px 100%, 100% 2px, 100% 2px, 25px 25px, 100% 100%, 100% 100%;
background-position: 0% 0%, 100% 25px, -25px 0%, 0px 100%, 100% 0%, -25px 0%, 100% 25px;
}
/* Just for demo */
*{
box-sizing: border-box;
}
div {
float: left;
color: black;
padding: 10px;
transition: all 1s;
margin: 10px;
}
div:hover {
height: 200px;
width: 300px;
}
body{
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<div class="cut-corner">Some content</div>
<div class="cut-corner filled">Some content</div>
You could use linear-gradient. Let's say the parent div had a background image, and you needed a div to sit on top of that with a gray background and a dog-eared left corner. You could do something like this:
.parent-div { background: url('/image.jpg'); }
.child-div {
background: #333;
background: linear-gradient(135deg, transparent 30px, #333 0);
}
See it on CodePen
Further reading:
CSS Gradients on CSS-Tricks
Beveled corners & negative border-radius with CSS3 gradients
I have an online generator for some of the below code: https://css-generators.com/custom-corners/
You can use mask and CSS variables to have better control over the whole shape. It's responsive, transparent and allow any kind of background:
.box {
--all:0px;
width:200px;
height:150px;
display:inline-block;
margin:10px;
background:red;
-webkit-mask:
linear-gradient( 45deg, transparent 0 var(--bottom-left,var(--all)) ,#fff 0) bottom left,
linear-gradient( -45deg, transparent 0 var(--bottom-right,var(--all)),#fff 0) bottom right,
linear-gradient( 135deg, transparent 0 var(--top-left,var(--all)) ,#fff 0) top left,
linear-gradient(-135deg, transparent 0 var(--top-right,var(--all)) ,#fff 0) top right;
-webkit-mask-size:50.5% 50.5%;
-webkit-mask-repeat:no-repeat;
}
body {
background:grey;
}
<div class="box" style="--top-left:20px"></div>
<div class="box" style="--top-right:20px;--bottom-right:50px;background:radial-gradient(red,yellow)"></div>
<div class="box" style="--all:30px;background:url(https://picsum.photos/id/104/200/200)"></div>
<div class="box" style="--all:30px;--bottom-right:0px;background:linear-gradient(red,blue)"></div>
<div class="box" style="--all:50%;width:150px;background:green"></div>
<div class="box" style="--all:12%;width:150px;background:repeating-linear-gradient(45deg,#000 0 10px,#fff 0 20px)"></div>
And below in case you want to consider border:
.box {
--all:0px;
--b:pink;
width:200px;
height:150px;
display:inline-block;
margin:10px;
border:5px solid var(--b);
background:
linear-gradient( 45deg, var(--b) 0 calc(var(--bottom-left,var(--all)) + 5px) ,transparent 0) bottom left /50% 50%,
linear-gradient( -45deg, var(--b) 0 calc(var(--bottom-right,var(--all)) + 5px),transparent 0) bottom right/50% 50%,
linear-gradient( 135deg, var(--b) 0 calc(var(--top-left,var(--all)) + 5px) ,transparent 0) top left /50% 50%,
linear-gradient(-135deg, var(--b) 0 calc(var(--top-right,var(--all)) + 5px) ,transparent 0) top right /50% 50%,
var(--img,red);
background-origin:border-box;
background-repeat:no-repeat;
-webkit-mask:
linear-gradient( 45deg, transparent 0 var(--bottom-left,var(--all)) ,#fff 0) bottom left,
linear-gradient( -45deg, transparent 0 var(--bottom-right,var(--all)),#fff 0) bottom right,
linear-gradient( 135deg, transparent 0 var(--top-left,var(--all)) ,#fff 0) top left,
linear-gradient(-135deg, transparent 0 var(--top-right,var(--all)) ,#fff 0) top right;
-webkit-mask-size:50.5% 50.5%;
-webkit-mask-repeat:no-repeat;
}
body {
background:grey;
}
<div class="box" style="--top-left:20px"></div>
<div class="box" style="--top-right:20px;--bottom-right:50px;--img:radial-gradient(red,yellow);--b:white;"></div>
<div class="box" style="--all:30px;--img:url(https://picsum.photos/id/104/200/200) center/cover;--b:orange;"></div>
<div class="box" style="--all:30px;--bottom-right:0px;--img:linear-gradient(red,blue)"></div>
<div class="box" style="--all:50%;width:150px;--img:green;--b:red;"></div>
<div class="box" style="--all:12%;width:150px;--img:repeating-linear-gradient(45deg,#000 0 10px,#fff 0 20px)"></div>
Let's also add some radius:
.box {
--all:0px;
--b:pink;
width:200px;
height:150px;
display:inline-block;
margin:10px;
filter:url(#round);
}
.box::before {
content:"";
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
background:var(--img,red);
-webkit-mask:
linear-gradient( 45deg, transparent 0 var(--bottom-left,var(--all)) ,#fff 0) bottom left,
linear-gradient( -45deg, transparent 0 var(--bottom-right,var(--all)),#fff 0) bottom right,
linear-gradient( 135deg, transparent 0 var(--top-left,var(--all)) ,#fff 0) top left,
linear-gradient(-135deg, transparent 0 var(--top-right,var(--all)) ,#fff 0) top right;
-webkit-mask-size:50.5% 50.5%;
-webkit-mask-repeat:no-repeat;
}
body {
background:grey;
}
<div class="box" style="--top-left:20px"></div>
<div class="box" style="--top-right:20px;--bottom-right:50px;--img:radial-gradient(red,yellow);--b:white;"></div>
<div class="box" style="--all:30px;--img:url(https://picsum.photos/id/104/200/200) center/cover;--b:orange;"></div>
<div class="box" style="--all:30px;--bottom-right:0px;--img:linear-gradient(red,blue)"></div>
<div class="box" style="--all:50%;width:150px;--img:green;--b:red;"></div>
<div class="box" style="--all:12%;width:150px;--img:repeating-linear-gradient(45deg,#000 0 10px,#fff 0 20px)"></div>
<svg style="visibility: hidden; position: absolute;" width="0" height="0" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="round">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo" />
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
This code allows you to cut corners on each side of the rectangle:
div {
display:block;
height: 300px;
width: 200px;
background: url('http://lorempixel.com/180/290/') no-repeat;
background-size:cover;
-webkit-clip-path: polygon(10px 0%, calc(100% - 10px) 0%, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0% calc(100% - 10px), 0% 10px);
clip-path: polygon(10px 0%, calc(100% - 10px) 0%, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0% calc(100% - 10px), 0% 10px);
}
http://jsfiddle.net/2bZAW/5552/
If you need a diagonal border instead of a diagonal corner, you can stack 2 divs with each a pseudo element:
DEMO
http://codepen.io/remcokalf/pen/BNxLMJ
.container {
padding: 100px 200px;
overflow: hidden;
}
div.diagonal {
background: #da1d00;
color: #fff;
font-family: Arial, Helvetica, sans-serif;
width: 300px;
height: 300px;
padding: 70px;
position: relative;
margin: 30px;
float: left;
}
div.diagonal2 {
background: #da1d00;
color: #fff;
font-family: Arial, Helvetica, sans-serif;
width: 300px;
height: 300px;
padding: 70px;
position: relative;
margin: 30px;
background: #da1d00 url(http://www.remcokalf.nl/background.jpg) left top;
background-size: cover;
float: left;
}
div.diagonal3 {
background: #da1d00;
color: #da1d00;
font-family: Arial, Helvetica, sans-serif;
width: 432px;
height: 432px;
padding: 4px;
position: relative;
margin: 30px;
float: left;
}
div.inside {
background: #fff;
color: #da1d00;
font-family: Arial, Helvetica, sans-serif;
width: 292px;
height: 292px;
padding: 70px;
position: relative;
}
div.diagonal:before,
div.diagonal2:before {
content: '';
position: absolute;
top: 0;
left: 0;
border-top: 80px solid #fff;
border-right: 80px solid transparent;
width: 0;
}
div.diagonal3:before {
content: '';
position: absolute;
top: 0;
left: 0;
border-top: 80px solid #da1d00;
border-right: 80px solid transparent;
width: 0;
z-index: 1;
}
div.inside:before {
content: '';
position: absolute;
top: -4px;
left: -4px;
border-top: 74px solid #fff;
border-right: 74px solid transparent;
width: 0;
z-index: 2;
}
h2 {
font-size: 30px;
line-height: 1.3em;
margin-bottom: 1em;
position: relative;
z-index: 1000;
}
p {
font-size: 16px;
line-height: 1.6em;
margin-bottom: 1.8em;
}
#grey {
width: 100%;
height: 400px;
background: #ccc;
position: relative;
margin-top: 100px;
}
#grey:before {
content: '';
position: absolute;
top: 0;
left: 0;
border-top: 80px solid #fff;
border-right: 80px solid #ccc;
width: 400px;
}
<div id="grey"></div>
<div class="container">
<div class="diagonal">
<h2>Header title</h2>
<p>Yes a CSS diagonal corner is possible</p>
</div>
<div class="diagonal2">
<h2>Header title</h2>
<p>Yes a CSS diagonal corner with background image is possible</p>
</div>
<div class="diagonal3">
<div class="inside">
<h2>Header title</h2>
<p>Yes a CSS diagonal border is even possible with an extra div</p>
</div>
</div>
</div>
We had the problem of different background colors for our cutted elements. And we only wanted upper right und bottom left corner.
body {
background-color: rgba(0,0,0,0.3)
}
.box {
position: relative;
display: block;
background: blue;
text-align: center;
color: white;
padding: 15px;
margin: 50px;
}
.box:before,
.box:after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 100%;
border-bottom: 15px solid blue;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
}
.box:before{
border-left: 15px solid blue;
}
.box:after{
border-right: 15px solid blue;
}
.box:after {
bottom: auto;
top: 100%;
border-bottom: none;
border-top: 15px solid blue;
}
/* Active box */
.box.active{
background: white;
color: black;
}
.active:before,
.active:after {
border-bottom: 15px solid white;
}
.active:before{
border-left: 15px solid white;
}
.active:after{
border-right: 15px solid white;
}
.active:after {
border-bottom: none;
border-top: 15px solid white;
}
<div class="box">
Some text goes here. Some text goes here. Some text goes here. Some text goes here.<br/>Some text goes here.<br/>Some text goes here.<br/>Some text goes here.<br/>Some text goes here.<br/>Some text goes here.<br/>
</div>
<div class="box">
Some text goes here.
</div>
<div class="box active">
Some text goes here.
<span class="border-bottom"></span>
</div>
<div class="box">
Some text goes here.
</div>
You can use clip-path, as Stewartside and Sviatoslav Oleksiv mentioned. To make things easy, I created a sass mixin:
#mixin cut-corners ($left-top, $right-top: 0px, $right-bottom: 0px, $left-bottom: 0px) {
clip-path: polygon($left-top 0%, calc(100% - #{$right-top}) 0%, 100% $right-top, 100% calc(100% - #{$right-bottom}), calc(100% - #{$right-bottom}) 100%, $left-bottom 100%, 0% calc(100% - #{$left-bottom}), 0% $left-top);
}
.cut-corners {
#include cut-corners(10px, 0, 25px, 50px);
}
According to Harry's linear-gradient solution (answered Oct 14 '15 at 9:55), it says that opacity background isn't possible, I tried it and yep, it isn't.
But! I found a workaround. No it's not super optimised, but it worked. So here's my solution. Since Harry doesn't use pseudo element, we can achieve this by creating one.
Set position relative to the container and create a pseudo element with the same linear-gradient properties. In other words, just clone it. Then put a transparent background for the container, and lets say a black background for the clone. Put a position absolute on it, a z-index of -1 and an opacity value (ie. 50%). It will do the job. Again it's a workaround and it's not perfect but it works just fine.
.cut-corner {
position: relative;
color: white;
background-repeat: no-repeat;
background-image: linear-gradient(white, white), linear-gradient(white, white), linear-gradient(white, white), linear-gradient(white, white), linear-gradient(to bottom left, transparent calc(50% - 1px), white calc(50% - 1px), white calc(50% + 1px), transparent calc(50% + 1px)), linear-gradient(transparent, transparent), linear-gradient(transparent, transparent);
background-size: 2px 100%, 2px 100%, 100% 2px, 100% 2px, 25px 25px, 100% 100%, 100% 100%;
background-position: 0% 0%, 100% 25px, -25px 0%, 0px 100%, 100% 0%, -25px 0%, 100% 25px;
}
.cut-corner:after {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
top: 0;
z-index: -1;
opacity: 0.5;
background-repeat: no-repeat;
background-image: linear-gradient(white, white), linear-gradient(white, white), linear-gradient(white, white), linear-gradient(white, white), linear-gradient(to bottom left, transparent calc(50% - 1px), white calc(50% - 1px), white calc(50% + 1px), black calc(50% + 1px)), linear-gradient(black, black), linear-gradient(black, black);
background-size: 2px 100%, 2px 100%, 100% 2px, 100% 2px, 25px 25px, 100% 100%, 100% 100%;
background-position: 0% 0%, 100% 25px, -25px 0%, 0px 100%, 100% 0%, -25px 0%, 100% 25px;
}
/* Just for demo */
div {
padding: 10px;
}
body{
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<div class="cut-corner">
Some content<br>
Some content<br>
Some content<br>
Some content
</div>
With a small edit to Joseph's code, the element does not require a solid background:
div {
height: 300px;
background: url('http://images2.layoutsparks.com/1/190037/serene-nature-scenery-blue.jpg');
position: relative;
}
div:before {
content: '';
position: absolute;
top: 0; right: 0;
border-top: 80px solid white;
border-left: 80px solid rgba(0,0,0,0);
width: 0;
}
http://jsfiddle.net/2bZAW/1921/
This use of 'rgba(0,0,0,0)' allows the inner 'corner' to be invisible
.
You can also edit the 4th parameter 'a', where 0 < a < 1, to have a shadow for more of a 'folded-corner' effect:
http://jsfiddle.net/2bZAW/1922/ (with shadow)
NOTE: RGBA color values are supported in IE9+, Firefox 3+, Chrome, Safari, and in Opera 10+.
by small modification of Joshep's code...You can use this code which seems like right corner folded down as per your requirement.
div {
height: 300px;
background: red;
position: relative;
}
div:before {
content: '';
position: absolute;
top: 0; right: 0;
border-top: 80px solid white;
border-left: 80px solid blue;
width: 0;
}
Another one solution:
html:
<div class="background">
<div class="container">Hello world!</div>
</div>
css:
.background {
position: relative;
width: 50px;
height: 50px;
border-right: 150px solid lightgreen;
border-bottom: 150px solid lightgreen;
border-radius: 10px;
}
.background::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
border: 25px solid lightgreen;
border-top-color: transparent;
border-left-color: transparent;
}
.container {
position: absolute;
padding-left: 25px;
padding-top: 25px;
font-size: 38px;
font-weight: bolder;
}
https://codepen.io/eggofevil/pen/KYaMjV
I recently cut off the top right corner and overlaid the tabs like folders. Complete code noob, so ignore the shitty code, but I did this by combining a square, a triangle, and a rectangle... This may or may not be a new approach, but hopefully, someone finds it helpful.
https://i.stack.imgur.com/qFMRz.png
Here is the HTML:
<!DOCTYPE html>
<html lang ="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="folders">
<div class="container">
<div class="triangleOne">
<p class="folderNames">Home</p>
</div>
<div class="triangleOneCut">
</div>
<div class="triangleOneFill">
</div>
</div>
<div class="container2">
<div class="triangleOne blue">
<p class="folderNames">About</p>
</div>
<div class="triangleOneCut blueCut">
</div>
<div class="triangleOneFill blue">
</div>
</div>
<div class="container3">
<div class="triangleOne green">
<p class="folderNames">Contact</p>
</div>
<div class="triangleOneCut greenCut">
</div>
<div class="triangleOneFill green">
</div>
</div>
</div>
</body>
</html>
Here is the CSS:
.triangleOne {
height: 50px;
width: 40px;
background: red;
border-radius: 5px 0px 0px 5px;
position: absolute;
}
.triangleOneCut {
content: '';
position: absolute;
top: 0; left: 40px;
border-top: 10px solid transparent;
border-left: 10px solid red;
width: 0;
}
.triangleOneFill {
content: '';
position: absolute;
top: 10px; left: 40px;
width: 10px;
height: 40px;
background-color: red;
border-radius: 0px 0px 5px 0px;
}
.container {
position: relative;
height: 50px;
width: 50px;
display: inline-block;
z-index: 3;
}
.container2 {
position: relative;
height: 50px;
width: 50px;
display: inline-block;
left: -10px;
z-index: 2;
}
.container3 {
position: relative;
height: 50px;
width: 50px;
display: inline-block;
left: -20px;
z-index: 1;
}
.blue {
background-color: blue;
}
.green {
background-color: green;
}
.blueCut {
border-left: 10px solid blue;
}
.greenCut {
border-left: 10px solid green;
}
.folders {
width: 160px;
height: 50px;
/* border: 10px solid white; */
margin: auto;
padding-left: 25px;
margin-top: 100px;
}
.folderNames {
text-align: right;
padding-left: 2px;
color: white;
margin-top: 1.5px;
font-family: monospace;
font-size: 6.5px;
border-bottom: double 1.5px white;
}
Here's a solution for if you don't want a solid-color background, i.e. just a border with square-cut corners.
.container {
width: 100px;
height: 100px;
background-color: white;
border: 1px solid black;
position: relative;
}
.border {
position: absolute;
width: 100%;
height: 100%;
}
.border:before {
content: '';
position: absolute;
border-top: 15px solid white;
border-left: 15px solid white;
width: 0;
}
.border:after {
content: '';
position: absolute;
width: 16px;
height: 1px;
background: black;
}
.tl:before { top: -5px; left: -5px; transform: rotate(-45deg); }
.tl:after { top: 5px; left: -3px; transform: rotate(-45deg);}
.tr:before { top: -5px; right: -5px; transform: rotate(45deg); }
.tr:after { top: 5px; right: -3px; transform: rotate(45deg); }
.bl:before { bottom: -5px; left: -5px; transform: rotate(45deg); }
.bl:after { bottom: 5px; left: -3px; transform: rotate(45deg); }
.br:before { bottom: -5px; right: -5px; transform: rotate(-45deg); }
.br:after { bottom: 5px; right: -3px; transform: rotate(-45deg); }
<html>
<body>
<div class="container">
<div class="border tl"></div>
<div class="border tr"></div>
<div class="border bl"></div>
<div class="border br"></div>
</div>
</body>
</html>

How to make a arrow using rectangle and triangle in css

How do I make this arrow using css?
I'm not able to remove the border of the third side of the triangle
Here is what I have so far:
#arrowbox
{
width: 200px;
height: 50px;
background-color:white;
margin-left:100px;
margin-top:100px;
position: relative;
border-style:solid;
}
#arrowbox:after {
left: 100%;
top: 20%;
content: " ";
height: 0;
width: 0;
position: absolute;
border-style:solid;
border-color: rgba(0, 128, 0, 0);
border-left-color: black;
border-width: 40px;
margin-top: -25px;
}
<div id="arrowbox"></div>
You can try something like this using pseudo-element and gradient:
.arrow {
width:300px;
height:100px;
margin:50px;
border:1px solid #000;
border-right:none;
position:relative;
}
.arrow:before {
content:"";
position:absolute;
right:-50px;
width:100px;
height:100px;
border-top:1px solid #000;
border-right:1px solid #000;
transform: rotate(45deg);
}
.arrow:after {
content:"";
position:absolute;
right:0;
top:-20px;
bottom:-20px;
width:1px;
background:
linear-gradient(#000,#000)top/100% 20px no-repeat,
linear-gradient(#000,#000)bottom/100% 20px no-repeat;
}
<div class="arrow">
</div>
You can also use polygon element of SVG to easily create it:
svg polygon {
stroke:#000;
fill:transparent;
}
<svg height="100" width="300">
<polygon points="0,20 0,80 200,80 200,100 300,50 200,0 200,20" />
</svg>
You could take advantage of the clip-path property, however, its not widely supported at this time: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path
#arrowbox {
width: 100px; height: 100px;
background: pink;
-webkit-clip-path: polygon(0% 35%, 75% 35%, 75% 25%, 99% 50%, 75% 75%, 75% 65%, 0% 65%);
clip-path: polygon(0% 35%, 75% 35%, 75% 25%, 99% 50%, 75% 75%, 75% 65%, 0% 65%);
}
<div id="arrowbox"></div>
For those looking for an SVG option, this would give you the luxury of filling it with a color if you wish, or add other styling attributes as needed:
svg { width: 150px; height: 150px; }
<svg viewBox="0 0 100 100" fill="none" stroke="black" stroke-width="1">
<path d="M 1,35 75,35 75,25 99,50 75,75 75,65 1,65z" />
</svg>
<hr>
<svg viewBox="0 0 100 100" fill="green" stroke="blue" stroke-width="2">
<path d="M 1,35 75,35 75,25 99,50 75,75 75,65 1,65z" />
</svg>

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