I'm wondering how I would go about creating a diagonal mask like effect. The mask would show all in the top left corner, hide the middle part, then show all in the bottom right corner. In the example, the mask would be on the .container element and mask out any children in the div as well.
I've looked at resources online, specifically here, and can't get this effect to work on non-image elements. Is there a different type of property to use in CSS to achieve this effect? I was thinking maybe SVG, but I'd like it to adapt to the width and height of the element, and wasn't sure how to pull that off.
JS Fiddle
.container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
mask: gradient(linear, left top, right bottom,
color-stop(0.00, rgba(0,0,0,1)),
color-stop(0.35, rgba(0,0,0,1)),
color-stop(0.50, rgba(0,0,0,0)),
color-stop(0.65, rgba(0,0,0,1)),
color-stop(1.00, rgba(0,0,0,1)));
}
.shape {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
background: red;
}
<div class="container">
<div class="shape"></div>
</div>
The mask would look something like this image.
Maybe so?
.container {
width:50%;
height:50%;
}
.rect1 {
fill: url('#grad1');
}
<div class="container">
<svg class="the-svg" viewBox="0 0 200 200" >
<defs>
<linearGradient id="grad1" x1="0" x2="1.0" y1="0" y2="1.0" >
<stop offset="0%" stop-color= "white"/>
<stop offset="35%" stop-color="white"/>
<stop offset="50%" stop-color="black"/>
<stop offset="65%" stop-color="white"/>
<stop offset="100%" stop-color="white"/>
</linearGradient>
</defs>
<rect class="rect1" x="0" y="0" width="100%" height="100%" />
</svg>
</div>
The solution is adaptive and works in all browsers, including Edge
Using mask-image works here. Updated JSFiddle.
.container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
-webkit-mask-image: linear-gradient(-45deg, black 0%, transparent 35% , transparent 50%, transparent 65%, black 100%);
mask-image: linear-gradient(-45deg, black 0%, transparent 35% , transparent 50%, transparent 65%, black 100%);
}
Related
I want to be able to round out the 3 leftmost corners on this shape that I have created, any idea how that can be done?
div {
position: absolute;
z-index: 1;
width: 423px;
height: 90px;
background-color: #b0102d;
color: white;
right: 0;
margin-top: 10vw;
-webkit-clip-path: polygon(100% 0%, 100% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
clip-path: polygon(100% 0%, 100% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}
<div></div>
use inset with round property :
inset(0% 45% 0% 45% round 10px)
An SVG filter can round any kind of clip-path. You simply need to apply it to a parent element. Adjust the stdDeviation to control the radius:
.box {
width: 423px;
height: 90px;
background-color: #b0102d;
color: white;
clip-path: polygon(100% 0%, 100% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}
.parent {
filter: url('#goo');
overflow:hidden;
position: fixed;
right:-50px;
z-index: 1;
margin-top: 10vw;
}
<div class="parent">
<div class="box"></div>
</div>
<svg style="visibility: hidden; position: absolute;" width="0" height="0" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="goo"><feGaussianBlur in="SourceGraphic" stdDeviation="8" 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>
Related: https://stackoverflow.com/a/65485455/8620333
I've recently found success experimenting with approaches like this...
SVG
<svg width="0" height="0">
<defs>
<clipPath id="clipped">
<circle cx="var(--myRad)" cy="var(--myRad)" r="var(--myRad)"></circle>
<circle cx="var(--myRad)" cy="calc(var(--myHeight) - var(--myRad))" r="var(--myRad)"></circle>
<circle cx="calc(var(--myWidth) - var(--myRad))" cy="calc(var(--myHeight) - var(--myRad))" r="var(--myRad)"></circle>
<circle cx="calc(var(--myWidth) - var(--myRad))" cy="var(--myRad)" r="var(--myRad)"></circle>
<rect y="var(--myRad)" width="var(--myWidth)" height="calc(var(--myHeight) - (2 * var(--myRad)))"></rect>
<rect x="var(--myRad)" width="calc(var(--myWidth) - (2 * var(--myRad)))" height="var(--myHeight)"></rect>
</clipPath>
</defs>
</svg>
CSS
.clipped {
--myWidth: 100vw;
--myHeight: 10rem;
--myRad: 2rem;
clip-path: url(#clipped);
}
I found this useful as compared to using border-radius with overflow set to hidden, because this approach doesn't create a BFC or break things like sticky position and css perspective effects. Also, this allows you to "inset" the position of the svg paths to clip inside the element with a "corner-radius" if you want.
You can also mess around with the circle to get some different effects.
-webkit-clip-path: circle(60.0% at 50% 10%);
clip-path: circle(50.0% at 50% 50%);
Codepen
Too bad you can't combine the polygon and circle... or maybe you can and I haven't played around with it enough to figure it out. HTH
clip-path: inset(45% 0% 33% 10% round 10px)
I don't have a comment option yes, so I'm writing it as an answer..
you need to write as many points as possible to round the corner. Nothig else...
for, example a few more points to make lower part bit rounder:
-webkit-clip-path: polygon(100% 0%, 100% 100%, 100% 100%, 25% 100%, 5% 70%,1% 60%, 0% 50%, 25% 0%);
oh, yes, or SVG as comment people here.. :)
You could use a child element and do a nested clip-path on that and the child's pseudo element. The parent will do a polygon clip on the shape first, then the pseudo will have an ellipse to round the borders. The clips will have a combined effect.
.parent, .parent div, .parent div:before {
width: 423px;
height: 90px;
position: absolute;
}
.parent {
right: 0;
background-image: linear-gradient(to right, transparent 210px, #b0102d 210px);
margin-top: 15vh;
}
.parent div {
clip-path: polygon(100% 0%, 100% 100%, 25% 100%, 0 50%, 25% 0);
}
.parent div:before {
content: "";
background-color: #b0102d;
clip-path: ellipse(200px 45px at 210px);
}
<div class="parent">
<div></div>
</div>
Here is the demo with some adaptations to illustrate what's going on:
.parent, .parent div, .parent div:before {
width: 423px;
height: 90px;
position: absolute;
}
.parent {
right: 0;
background-image: linear-gradient(to right, transparent 210px, yellow 210px);
margin-top: 15vh;
}
.parent div {
background-color: blue;
clip-path: polygon(90% 0%, 90% 100%, 25% 100%, 0 50%, 25% 0);
}
.parent div:before {
content: "";
background-color: #b0102d;
clip-path: ellipse(200px 45px at 210px);
}
<div class="parent">
<div></div>
</div>
The horizontal size and position of the ellipse can be used to get a different effect on the edges. Note that the background starting postion of the parent needs to be adjusted to the same value as the placement of the ellipse (last value in the clip-path) because it fills up whatever gets clipped off on the right side. This can be visualised by removing background-color: blue from .parent div in the second demo.
Here is an additional Codepen to to try it out.
I'm trying to draw several grids of various sizes but I'm having an issue with Firefox - linear-gradient is breaking up for me in many places.
It's working all fine on Google Chrome with any units provided (px/mm/%/rounded/float) but it's doing some funny stuff on Firefox. I've tried using different units/rounding/prefixed/3d hacks but none of this is working.
div {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-image: linear-gradient(to right, black 1px, transparent 1px),
linear-gradient(to bottom, black 1px, transparent 1px);
background-size: 5mm 5mm;
}
<div></div>
A repeating gradient should give better result but it's always tricky when it comes to small values like 1px with gradients
div {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-image:
repeating-linear-gradient(to right, black 0 1px, transparent 0 5mm),
repeating-linear-gradient(to bottom, black 0 1px, transparent 0 5mm);
}
<div></div>
you can also consider an SVG here (adjust the viewBox, width and height or the rect until you get a good result)
div {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='black'> <rect x='0' y='0' width='1' height='100%' /> <rect x='0' y='0' width='100%' height='1'/></svg>")
0 0/5mm 5mm;
}
<div></div>
Also like below with only SVG:
svg {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
}
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
<rect x='0' y='0' width='1' height='100%' />
<rect x='0' y='0' width='100%' height='1'/>
</pattern>
</defs>
<rect width="3000" height="3000" fill="url(#grid)" />
</svg>
I want to create a triangle pointing down in css by using clip-path: polygon(...) and apply a gradient on it using background-image: linear-gradient(...).
This all works fine but I need this shape as the background of my web page.
It needs to always be centered and it needs to clip/cut off the left and right edges that do not fit in the browser window. The triangle should not re-scale itself; I want to preserve the steepness of the triangle's edges and the height of the triangle should not change:
As illustrated, the triangle should stay the same width and height even when the browser window is too small to contain it.
So far I have:
div.main-background {
position: absolute;
z-index: -1;
top: 0;
height: 500px;
width: 100%;
background-image: linear-gradient(to bottom, #65AAB0, #AEE2B6);
background-attachment: fixed;
background-position-x: center;
background-size: 1400px 500px;
clip-path: polygon(50% 80%, 0 0, 1400px 0);
}
<div class="main-background"></div>
but this is clearly wrong.
You can do this with SVG
html,
body {
margin: 0
}
svg {
width: 100%;
}
<svg viewBox="0 0 1920 400" height="400" preserveAspectRatio="xMidYMax slice">
<defs>
<linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="#65AAB0"></stop>
<stop offset="100%" stop-color="#AEE2B6"></stop>
</linearGradient>
</defs>
<polygon points="0,0 960,400 1920,0" fill="url(#Gradient1)"></polygon>
</svg>
Using viewport units, is this what you want? Works on any resolution.
div.main-background {
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
height: 35.71vw;
background-image: linear-gradient(to bottom, #65AAB0, #AEE2B6);
background-attachment: fixed;
background-position: center;
clip-path: polygon(50% 80%, 0 0, 100vw 0);
}
<div class="main-background"></div>
You can try multiple background like below. I made the triangle to have a width of 600px and a height of 300pxthat you can easily adjust.
body {
background:
linear-gradient(to bottom right,transparent 49.8%,#fff 50%) calc(50% + 150px) 0 /300px 300px,
linear-gradient(to bottom left, transparent 49.8%,#fff 50%) calc(50% - 150px) 0 /300px 300px,
linear-gradient(to bottom, #65AAB0, #AEE2B6)top center/ 600px 300px;
background-repeat:no-repeat;
}
Easier with CSS variable :
body {
--w:800px;
--h:300px;
background:
linear-gradient(to bottom right,transparent 49.8%,#fff 50%) calc(50% + calc(var(--w)/4)) 0 /calc(var(--w)/2) var(--h),
linear-gradient(to bottom left, transparent 49.8%,#fff 50%) calc(50% - calc(var(--w)/4)) 0 /calc(var(--w)/2) var(--h),
linear-gradient(to bottom, #65AAB0, #AEE2B6)top center/ var(--w) var(--h);
background-repeat:no-repeat;
}
You are going about it the wrong way. Your div contains no content and is thus simply decorative cruft. If you want a page background of certain appearance, then whatever appearance the background should have, goes into the background property of the document element (typically body, or html).
Get rid of your useless div.main-background which serves no purpose whatsoever, and use the following background image, either standalone (in its own SVG file) or inline using a data: URI:
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1">
<polygon fill="lime" points="0,0 0.5,0.4 1,0" />
</svg>
The following CSS declaration will use the above as a background image, from your explanation the background size should be a definite length (as opposed to one relative to viewport dimensions), I will use 40em because I didn't pick any clues from your question:
body {
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"><polygon fill="lime" points="0,0 0.5,0.4 1,0" /></svg>');
background-size: 40em;
background-repeat: no-repeat;
background-position: top center;
}
Alternatively, you can use a standalone SVG file, then your background rule will be different:
background: url(<URL-of-SVG-file>);
You can add the gradient easily by editing SVG content, it's a basic SVG feature, one of the other answers here even demonstrates how.
Maybe you can use that kind of trick with just CSS and an after pseudo-element:
body {
overflow: hidden;
}
.arrow-down {
--w:800px;
--h:300px;
position: relative;
width: var(--w);
height: var(--h);
margin-left: 50%;
transform: translate(-50%, 0);
background-image: linear-gradient(to bottom, #65AAB0, #AEE2B6);
}
.arrow-down::after{
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border: solid white;
border-width: calc(var(--h)/2) calc(var(--w)/2);
border-top-color: transparent;
}
<body>
<div class="arrow-down"></div>
</body>
Of course, you can tweak width and height to better suit your needs.
I have read up on various methods and played with the Clippy tool, the problem is the browser support just isn't there yet. What would be the best method for accomplishing the look of the image below with CSS? I am trying to add a shape as bottom-border as you can see in the image below right after the blue background image. Is there a way I can do this that most recent major browsers support through CSS?
What I've tried (doesn't seem to work in Chrome and others):
.element {
-webkit-clip-path: polygon(50% 0%, 100% 0, 100% 86%, 75% 100%, 0 85%, 0 0);
clip-path: polygon(50% 0%, 100% 0, 100% 86%, 75% 100%, 0 85%, 0 0);
}
The desired result would look something like:
Both dippas' answer and the demo in misterManSam's comment are good but they would work only if the page background is a solid color (which can then be used as border's color or within the gradient). They would run into problems when the page's background is either an image (or) a gradient and they should show through the cutout portion of the shape.
For such cases I would recommend using SVG instead of CSS because it is so complex to create it with CSS that it is not actually worth the effort. Though you've asked for CSS, I will detail these SVG methods here just in case you want to use them (or atleast some future readers might find it helpful).
With SVG:
With SVG we can either create a path and fill it with the image (or) use a SVG mask for creating the shape. (Note: CSS clip-path using SVG is still a no-go due to lack of support in IE.)
Below snippet uses SVG path element to create the shape and then fill it with the image.
svg {
height: 200px;
width: 100%;
}
path {
fill: url(#image);
}
/* Just for demo */
path:hover{
cursor: pointer;
}
body {
min-height: 100vh;
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<svg viewBox='0 0 1024 200' preserveAspectRatio='none'>
<defs>
<pattern id='image' height='200' width='1024' patternUnits='userSpaceOnUse'>
<image xlink:href='http://lorempixel.com/1024/200/nature/3' height='200' width='1024' />
</pattern>
</defs>
<path d='M0,0 1024,0 1024,150 716.8,200 0,150z' />
</svg>
The following snippet uses SVG mask. The difference between using a path with an image fill and a mask is the hover area. With a path the hover effects are restricted to the shape boundary whereas with a mask, the image is still a rectangle (or square) and so hover effects are triggered even outside.
svg {
height: 200px;
width: 100%;
}
image {
mask: url(#masker);
}
/* Just for demo */
image:hover{
cursor: pointer;
}
body {
min-height: 100vh;
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<svg viewBox='0 0 1024 200' preserveAspectRatio='none'>
<defs>
<mask id='masker' x='0' y='0' width='1024' height='200'>
<polygon points='0,0 1024,0 1024,200 0,200z' fill="#fff" />
<path d='M0,150 716.8,200 1024,150 1024,200 0,200z' fill="#000" />
</mask>
</defs>
<image xlink:href='http://lorempixel.com/1024/200/nature/3' height='200' width='1024' />
</svg>
With CSS:
The below option is our best bet with pure CSS but unfortunately it has poor browser support. It uses CSS linear-gradient as mask images to hide the portions that are not required. This method works only in Webkit powered browsers for now and so is a no-go.
.shape {
height: 200px;
width: 100%;
background-image: url(http://lorempixel.com/1200/200/nature/3);
-webkit-mask-image: linear-gradient(to top right, transparent 49.5%, white 50.5%), linear-gradient(to top left, transparent 49.5%, white 50.5%), linear-gradient(white, white);
mask-image: linear-gradient(to top right, transparent 49.5%, white 50.5%), linear-gradient(to top left, transparent 49.5%, white 50.5%), linear-gradient(white, white);
-webkit-mask-size: 70.5% 30%, 30% 30%, 100% 70%;
-webkit-mask-position: bottom left, bottom right, top left;
-webkit-mask-repeat: no-repeat;
}
body {
min-height: 100vh;
background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<div class='shape'></div>
Other attempts to produce a transparent cut run into problems if the shape has to be responsive. For example, the below snippet uses very complex transformations, positioning etc to achieve this shape but it is not responsive (view in full page mode). I wouldn't have recommended this method even if the shape was responsive (due to complexities involved) but the lack of responsiveness means this is a no-go.
.shape {
position: relative;
height: 200px;
width: 100%;
overflow: hidden;
}
.shape-left,
.shape-right,
.shape img {
position: absolute;
height: 100%;
}
.shape-left {
width: 75%;
transform: skewY(5deg);
overflow: hidden;
}
.shape-left img {
top: -7%;
bottom: 0px;
width: 133.3%;
transform: skewY(-5deg);
}
.shape-left,
.shape-left img {
transform-origin: bottom right;
backface-visibility: hidden;
}
.shape-right {
right: 0%;
width: 25%;
transform: skewY(-10deg);
overflow: hidden;
}
.shape-right img {
top: -13.5%;
left: -300%;
width: 400%;
transform: skewY(10deg);
}
.shape-right {
transform-origin: bottom left;
backface-visibility: hidden;
}
/* just for demo */
.reference {
height: 200px;
width: 100%;
}
.reference img {
height: 100%;
width: 100%;
}
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
background-image:radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<div class='shape'>
<div class='shape-left'>
<img src='http://lorempixel.com/800/200/nature/3' />
</div>
<div class='shape-right'>
<img src='http://lorempixel.com/800/200/nature/3' />
</div>
</div>
<hr>
<div class='reference'>
<img src='http://lorempixel.com/800/200/nature/3' />
</div>
Note: This may have been the item that misterManSam was referring to in comments but I feel the needs are a bit different here even though both involve creating unusual borders.
you can use a background-image on a div and two shapes using it pseudo-selectors :before/:after
Something like this:
.bg {
background: url(http://lorempixel.com/1600/900) no-repeat center top;
min-height: 100px;
min-width: 200px;
position: relative
}
.bg:before {
content: "";
border-bottom: 65px solid white;
border-right: 575px solid rgba(0, 0, 0, 0);
position: absolute;
left: 0;
bottom: 0;
}
.bg:after {
content: "";
border-bottom: 65px solid white;
border-left: 200px solid rgba(0, 0, 0, 0);
position: absolute;
right: 0;
bottom: 0;
}
<div class="bg"></div>
I think the easiest way to do it is with pseudo elements on the parent div element. This is basic CSS knowledge and can be implemented very easily. The parent div needs to have the position: relative; property set and the rest is done by the ::before and ::after elements.
.background::before {
transform: rotate(10deg);
position: absolute;
}
Example
Hope this helps.
I'm trying to adjust a triangle that will cover 50% of the parent container from corner to corner, no matter what the ratios of the box are the triangle can be skewered.
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" preserveAspectRatio="xMinYMin meet" viewBox="0,0,100,100">
<polygon points="100,100 100,0 0,100"/>
</svg>
</div>
.container {
height:160px;
background-color:#ccc;
margin-top:10px;
}
I've setup a fiddle with the code, I'm trying to replicate the same behaviour that I was able to achieve with css, the reason why I want to go the route of svg is to stop the line from getting pixelation this is the previous code of css.
How to achieve the same result in css
<div class="parent">
<div class="arrow-right"></div>
</div>
.parent {
position:relative;
width:230px;
height:150px;
background-color:red;
}
.arrow-right {
position: absolute;
width: 100%;
height: 100%;
left: 0px;
bottom: 0px;
background: linear-gradient(to left top, #333 50%, transparent 50%);
opacity: 0.5;
}
How do I change the viewBox to allow for the polygon shape to not stay in proportion?
You need to add preserveAspectRatio="none" and stretch svg svg {width:100%; height:100%}
fiddle