How to create reuleaux triangle shape using CSS3 - css

I need a help to make reuleaux triangle shape using CSS3 like below the image. The shape has a white border around. How is it possible?

CSS is not the right tool for creating such shapes even though they can be created using it. They will require multiple real/pseudo-elements, transforms etc and even then maintenance of the curves, their radii etc are very tricky. It gets even more complex when you require borders around them or have to place images or gradients inside them.
The best and recommended tool for creating such shapes is SVG as they have the following pros:
SVGs are scalable by nature and so are very good for responsive designs
SVG shapes can take images or gradients as fills
Curve and radii control is very optimum
Below is a sample snippet for creating the reuleaux triangle shape using SVG. All it needs is a single path element with 3 Quadratic Curveto commands.
svg {
height: 200px;
width: 200px;
}
path {
fill: steelblue;
stroke: white;
stroke-width: 2;
}
path.image {
fill: url(#g-image);
}
body {
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<svg viewBox="0 0 105 105" preserveAspectRatio="none">
<path d="M2,15 q50,-25 100,0 q0,50 -50,85 q-50,-30 -50,-85z" />
</svg>
<svg viewBox="0 0 105 105" preserveAspectRatio="none">
<defs>
<pattern id="g-image" width="1" height="1" patternUnits="objectBoundingBox">
<image xlink:href="http://lorempixel.com/200/200/nature/4" width="200" height="200" />
</pattern>
</defs>
<path d="M2,15 q50,-25 100,0 q0,50 -50,85 q-50,-30 -50,-85z" class="image" />
</svg>
The same can be achieved by using CSS Clip-path with inline SVG for the path also but the support is non-existent in IE for this and hence it is not recommended.
div {
position: relative;
background: white;
height: 200px;
width: 200px;
-webkit-clip-path: url(#clipper);
clip-path: url(#clipper);
}
div:after {
position: absolute;
content: '';
height: calc(100% - 4px);
width: calc(100% - 4px);
top: 2px;
left: 2px;
background: steelblue;
-webkit-clip-path: url(#clipper);
clip-path: url(#clipper);
}
div.image:after{
background: url(http://lorempixel.com/200/200);
}
body {
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
/* Just for demo */
div{
display: inline-block;
}
<svg width="0" height="0">
<defs>
<clipPath id="clipper" clipPathUnits="objectBoundingBox">
<path d="M0,0.15 q0.5,-0.25 1,0 q0,0.5 -0.5,0.85 q-0.5,-0.3 -0.5,-0.85z" />
</clipPath>
</defs>
</svg>
<div></div>
<div class='image'></div>

SVG solution
svg {
border: 1px solid black;
}
<svg width="400px" viewBox="0 0 100 100">
<path stroke="black" d="m50 90,
q -40 -20, -40 -80,
q 40 -10, 80 0,
q 0 60, -40 80z" />
</svg>

This shape is possible with pure CSS in a single element with a little bit of creativity.
It is not exactly the shape as above as it has rounded corners but its still pretty darn close.
div {
width: 200px;
height: 200px;
background: blue;
border-radius: 75% 75% 80% 80% / 15% 15% 150% 150%;
}
<div></div>
Here is another possible way to do it
div {
width: 200px;
height: 200px;
background: blue;
border-radius: 10% 100% 100% 0 / 100% 100% 10% 0;
transform: rotate(-45deg);
margin-left: 50px;
}
<div></div>

Related

Why does the svg shape dissapear after setting the transform-origin to 50%? [duplicate]

This question already has an answer here:
How to use transform-origin in conjunction with SVGs? [duplicate]
(1 answer)
Closed last month.
Transform-origin 50% 50% sets the center of scaling to the center of the element. So when I scale the element, it should just scale from the circles center meaning, the viewbox stays in place, and only the element scales from the elements center, but that's not what is happening. Is this actually setting the point of origin for the whole viewbox of the element?
svg{
border: 1px solid red;
}
circle{
fill: red;
fill-opacity: 50%;
stroke: red;
stroke-width: 1px;
}
circle:hover{
transform:scale(2);
transform-origin: 50% 50%;
}
<svg width="800" height="600" viewBox="0 0 800 600">
<circle cx="50" cy="50" r="20"/>
</svg>
Here, I tried to explain both the problem and possible solution. I have considered 50px 50px in the transform. Because, it is the dimension of the circle. Hope this helps.. Good luck!
svg {
border: 1px solid red;
}
circle {
fill: red;
fill-opacity: 50%;
stroke: red;
stroke-width: 1px;
}
.problem circle:hover {
transform: scale(2);
transform-origin: 50% 50%;
}
.solution circle:hover {
transform: scale(2);
transform-origin: 50px 50px;
}
<svg class="problem" width="200" height="200" viewBox="0 0 200 200">
<circle cx="50" cy="50" r="20"/>
</svg>
<svg class="solution" width="200" height="200" viewBox="0 0 200 200">
<circle cx="50" cy="50" r="20"/>
</svg>

CSS: mix-blend-mode = color-dodge not working in Chrome but okay in Firefox

I am trying to use an effect where the same shape (SVG in my case) at different locations is overlayed, and its colors should be "color dodged". The shape is coming in the primary colors red, green and blue. Where all shapes meet, the color is white, and in other places combinations occur. I've created a pen at https://codepen.io/anon/pen/dgKQqz to demonstrate. In short, the styles:
body { background-color: #222; }
.demo { mix-blend-mode: color-dodge; }
.center { position: fixed; top: 50%; left: 50%; }
.pr { transform: translate(-30%, -50%); }
.pg { transform: translate(-80%, -50%); }
.pb { transform: translate(-50%, -30%); }
And the shapes:
<svg class="demo center pr" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#ff0000" />
</svg>
<svg class="demo center pg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#00ff00" />
</svg>
<svg class="demo center pb" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="#0000ff" />
</svg>
This works as I expect in Firefox (62), but no mixing seems to take place in Chrome (70). The issue is not with SVG, as even regular text in a div element behaves like described.
Am I doing something wrong, can this be achieved so it works in both browsers, or is this a Chrome bug?
In fact using the SVGs as background-image is working:
.demo {
width:100px;
height:100px;
mix-blend-mode: color-dodge;
}
body {
background-color: #222;
}
.center {
position: fixed;
top: 50%;
left: 50%;
}
.pr {
transform: translate(-30%, -50%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%23ff0000' /%3E%3C/svg%3E");
}
.pg {
transform: translate(-80%, -50%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%2300ff00' /%3E%3C/svg%3E");
}
.pb {
transform: translate(-50%, -30%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%230000ff' /%3E%3C/svg%3E");
}
<div class="demo center pr"></div>
<div class="demo center pg"></div>
<div class="demo center pb"></div>

Pure CSS gradient circle border

I have this UI requirement
At the moment, I have a working solution of a div (with a fixed height and width and a background image for the outer gradient border) and a pseudo element, positioned absolute with a background image of the inner border.
.div {
position: relative;
width: 254px;
height: 254px;
border: 2px solid transparent;
border-radius: 50%;
background: url(../img/gradient_border_circle.png) no-repeat 50%;
}
div:before {
content: "";
position: absolute;
top: 50%;
transform: translate(-50%,-50%);
left: 50%;
width: 98px;
height: 98px;
border-radius: 50%;
background: url(../img/gradient_border_circle_inner.png) no-repeat 50%;
}
However, am looking for a more elegant solution (pure css or svg gradient?) without the use of background images where the gradient can scale with no pixelation.
I have researched and closest I have come across is https://codepen.io/nordstromdesign/pen/QNrBRM and Possible to use border-radius together with a border-image which has a gradient? But I need a solution where the centre is transparent in order to show through the page's background
Update: Ideally, am looking for a solution with relatively good support in all modern browsers.
SVG is the recommended way to create a circle shape and draw gradient outline / border around it.
SVG has a circle element that can be used to draw a circle shape. This shape can be filled and outlined with a solid color, gradient or pattern.
* {box-sizing: border-box;}
body {
background: linear-gradient(#333, #999);
text-align: center;
min-height: 100vh;
padding-top: 10px;
margin: 0;
}
svg {vertical-align: top;}
<svg width="210" height="210">
<defs>
<linearGradient id="grad1" x1="0" y1="1" x2="1" y2="0">
<stop offset="0" stop-color="#f5d700" />
<stop offset="1" stop-color="#0065da" />
</linearGradient>
<linearGradient id="grad2" xlink:href="#grad1" x1="1" y1="0" x2="0" y2="1"></linearGradient>
</defs>
<g fill="none">
<circle cx="100" cy="100" r="95" stroke="url(#grad1)" stroke-width="2" />
<circle cx="100" cy="100" r="40" stroke="url(#grad2)" stroke-width="5" />
</g>
</svg>
You can use a mask to achieve what you're looking for. You will need an SVG file with a transparent circle. Here I used an image from the internet, but you can make your own to accommodate your needs:
mask: url(circle.svg);
CodePen (set the background to red to show transparency)
mask reference
making your own masks
Here is a CSS only solution that should work fine in all modern browsers (tested on Chrome, Firefox and Edge)
.box {
--it:20px; /* thickness of inner gradient */
--ot:10px; /* thickness of outer gradient */
--s:30%; /* starting point of inner gradient */
width:200px;
display:inline-flex;
box-sizing:border-box;
border-radius:50%;
border:var(--ot) solid transparent;
background:
/* inner gradient clipped to the padding area */
conic-gradient(red,blue,green,red) padding-box,
/* outer gradient visible on the border area */
conic-gradient(purple,yellow,orange,purple) border-box;
-webkit-mask:radial-gradient(farthest-side,
transparent var(--s),
#fff calc(var(--s) + 1px)
calc(var(--s) + var(--it)),
#fff0 calc(var(--s) + var(--it) + 1px)
calc(100% - var(--ot)),
#fff calc(100% - var(--ot) + 1px));
}
/* keep the ratio */
.box::before {
content:"";
padding-top:100%;
}
body {
background:pink;
}
<div class="box"></div>
<div class="box" style="--s:5%;--ot:20px;width:150px;"></div>
<div class="box" style="--s:calc(100% - 20px);--it:10px;width:220px;"></div>
<div class="box" style="--s:0%;--it:50%;width:80px;"></div>
I am adding 1px in the calculation to avoid jagged edges. You can replace the conic-gradient() with another type of gradient or even an image

Double trapezium-like shape

I am busting my head with getting this double trapezium like shape on a webpage. The CSS class seen below creates a trapezium that could be duplicated and rotaded, I'm however insure how to expand it to the full width of the browser and make it responsive.
.warpedbanner {
border-right: 150px solid red;
border-top: 50px solid transparent;
border-bottom: 50px solid transparent;
height: 4em;
width: 4px;
}
You can check LINK for an shape.
div {
width: 400px;
height: 150px;
clip-path: polygon(50% 0%, 100% 15%, 100% 84%, 50% 100%, 0 85%, 0 18%);
background-color: red;
}
<div></div>
The above snippet will work in chrome but not in FireFox. Snippet which is working in both browser is given below
div {
width: 400px;
height: 150px;
background-color: red;
-webkit-clip-path: url("#clipping");
clip-path: url("#clipping");
}
<div></div>
<svg width='0' height='0'>
<defs>
<clipPath id="clipping" clipPathUnits="objectBoundingBox">
<polygon points="0 0.25, 0.5 0, 1 0.25, 1 0.75, 0.5 1,0 0.75" />
</clipPath>
</defs>
</svg>

Border gradient from once color to another uing css [duplicate]

I have a problem with CSS3. I don't know how to make a diagonal round gradient border like that:
I found something like this:
.box {
width: 250px;
height: 250px;
margin: auto;
background: #eee;
border: 20px solid transparent;
-moz-border-image: -moz-linear-gradient(top left, #3acfd5 0%, #3a4ed5 100%);
-webkit-border-image: -webkit-linear-gradient(top left, #3acfd5 0%, #3a4ed5 100%);
border-image: linear-gradient(to bottom right, #3acfd5 0%, #3a4ed5 100%);
border-image-slice: 1;
}
<div class="box"></div>
But unfortunately this works only with squares.
Any help would be appreciated.
Conical gradient is a gradient which goes along the circular arc around a center. This is what we see in color wheels. As Paulie_D had noted, these are currently not possible with CSS but Lea Verou has developed a polyfill for it.
Having said that, what you are looking for doesn't seem to be a conical gradient, it is normal angled linear gradient but applied only to the borders.
This cannot be achieved through CSS border-image property because of how it is intended to work as per specs.
A box's backgrounds, but not its border-image, are clipped to the appropriate curve
If the center portion of the circle is a solid color then the approach mentioned in Vitorino's answer can be used. If it is not a solid color (that is, the page background is a gradient or an image which needs to show through) then it would not help. The following approaches can be used for that case.
Using Mask Image:
This approach uses a circular mask image to mask the inner portion of the circle. This makes it look as though only the border has the gradient applied to it. The drawback is that this feature is currently supported only in Webkit powered browsers.
.border-gradient-mask {
height: 200px;
width: 200px;
border-radius: 50%;
background-image: linear-gradient(to bottom left, #7B73A4 0%, #150E5E 100%);
-webkit-mask-image: radial-gradient(circle at center, transparent 57%, white 58%);
mask-image: radial-gradient(circle at center, transparent 57%, white 58%);
}
body {
background: radial-gradient(circle at center, sandybrown, chocolate);
}
<div class="border-gradient-mask"></div>
Using SVG Shape or Mask:
The other approach is to use SVG circle element to create the circle and then assign the gradient to the stroke property. The gradient also has a gradientTransform applied to it because that is the only way to produce angled linear gradients with SVG.
.border-gradient-svg {
position: relative;
height: 200px;
width: 200px;
border-radius: 50%;
}
.border-gradient-svg svg {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
.border-gradient-svg circle {
fill: transparent;
stroke: url(#grad);
stroke-width: 8;
}
body {
background: radial-gradient(circle at center, sandybrown, chocolate);
}
<div class="border-gradient-svg">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="grad" gradientUnits="objectBoundingBox" gradientTransform="rotate(135 0.5 0.5)">
<stop offset="0%" stop-color="#7B73A4" />
<stop offset="100%" stop-color="#150E5E" />
</linearGradient>
</defs>
<circle r="46" cx="50" cy="50" />
</svg>
</div>
The same can be achieved by using SVG mask also. All that is needed is to create a mask with two circle elements, fill the larger circle with white, smaller circle with black and then apply the mask to our original circle element. The area occupied by smaller circle (with black fill) will be transparent.
.border-gradient-svg {
position: relative;
height: 200px;
width: 200px;
border-radius: 50%;
}
.border-gradient-svg svg {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
.border-gradient-svg .grad-border {
fill: url(#grad);
mask: url(#masker);
}
body {
background: radial-gradient(circle at center, sandybrown, chocolate);
}
<div class="border-gradient-svg">
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="grad" gradientUnits="objectBoundingBox" gradientTransform="rotate(135 0.5 0.5)">
<stop offset="0%" stop-color="#7B73A4" />
<stop offset="100%" stop-color="#150E5E" />
</linearGradient>
<mask id="masker" x="0" y="0" width="100" height="100">
<circle r="50" cx="50" cy="50" fill="#fff" />
<circle r="42" cx="50" cy="50" fill="#000" />
</mask>
</defs>
<circle r="50" cx="50" cy="50" class="grad-border"/>
</svg>
</div>
Using Clip Path:
Another approach to creating this would be to use a clip-path (with inline SVG) with clip-rule set to evenodd. Advantage of clip path solution over the others is that this will trigger hover effects only while hovering on filled area (and not the transparent area). The drawback is that IE doesn't support clip paths (even with SVG).
.border-gradient-clip {
height: 200px;
width: 200px;
border-radius: 50%;
background-image: linear-gradient(to bottom left, #7B73A4 0%, #150E5E 100%);
-webkit-clip-path: url(#clipper);
clip-path: url(#clipper);
}
body {
background: radial-gradient(circle at center, sandybrown, chocolate);
}
<svg width="0" height="0">
<defs>
<clipPath id="clipper" clipPathUnits="objectBoundingBox">
<path d="M0,0.5 a0.5,0.5 0 1,0 1,0 a0.5,0.5 0 1,0 -1,0z M0.08,0.5 a0.42,0.42 0 1,0 0.84,0 a0.42,0.42 0 1,0 -0.84,0z" clip-rule="evenodd" />
</clipPath>
</defs>
</svg>
<div class="border-gradient-clip"></div>
You can try something like this i have used a pseudo element with -ve z-index
Note: the background is not transparent as i have used a background-color for inner element
.box {
width: 250px;
height: 250px;
position: relative;
margin: auto;
margin: 30px;
border-radius: 50%;
background: #fff;
}
.box:after {
content: '';
position: absolute;
top: -15px;
bottom: -15px;
right: -15px;
left: -15px;
background-image: linear-gradient(to bottom left, #7B73A4 0%, #150E5E 100%);
z-index: -1;
border-radius: inherit;
}
<div class="box"></div>

Resources