I would like to create HTML element like on image here:
A problem is the DIV element has polygon shape instead of regular rectangle, will be placed above other elements as something like popup and inside that element there is necessary to show an image with rectangular shape in source but showed on web like filling all space included triangle on the left side.
Do you think is there any possibility to realize that without preparing showed images as transparent PNGs in proper polygon format? Only by CSS3 transform or use canvas or SVG?
One method could be to split the image into two containers which are 50% the size of the parent, transform each of them separately and position the backgrounds to look like they are one single image. The transform could either be a skew (used in the answer) or a perspective based rotation.
Note that since we are transforming the container, we have to apply the reverse effect to the actual image for it to look normal.
.image {
position: relative;
height: 150px;
width: 450px;
overflow: hidden;
}
.top-container,
.bottom-container {
position: absolute;
left: 0px;
height: 50%;
width: 100%;
overflow: hidden;
backface-visibility: hidden;
}
.top-container {
top: 0px;
transform-origin: right bottom;
transform: skew(-20deg);
}
.bottom-container {
bottom: 0px;
transform-origin: right top;
transform: skew(20deg);
background-position: 0% 100%;
}
.top-container:after,
.bottom-container:after {
position: absolute;
content: '';
height: 100%;
width: 100%;
left: -14px; /* tan(20) * (height/2) / 2 */
background: url(http://lorempixel.com/450/150);
background-size: 100% 200%;
}
.top-container:after {
top: 0px;
transform: skew(20deg);
}
.bottom-container:after {
bottom: 0px;
transform: skew(-20deg);
background-position: 0% 100%;
}
/* Just for demo */
body {
background: linear-gradient(90deg, crimson, indianred, purple);
}
.image2 {
margin-top: 10px;
height: 150px;
width: 450px;
background: url(http://lorempixel.com/450/150);
}
<div class="image">
<div class='top-container'></div>
<div class='bottom-container'></div>
</div>
<!-- this is the actual image for comparison -->
<h3>Original Image</h3>
<div class='image2'></div>
I was about to suggest usage of SVG and clipPath but since Persijn has already posted that sample, I have added below a different version with polygon.
.vector {
position: relative;
height: 150px;
width: 450px;
}
svg {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
polygon {
fill: url(#image);
}
/* Just for demo */
body {
background: linear-gradient(90deg, crimson, indianred, purple);
}
<div class='vector'>
<svg viewBox='0 0 450 150' preserveAspectRatio='none'>
<defs>
<pattern id='image' height='150' width='450' patternUnits='userSpaceOnUse'>
<image xlink:href='http://lorempixel.com/450/150' height='150' width='450' />
</pattern>
</defs>
<polygon points='15,0 450,0 450,150 15,150 0,75' />
</svg>
</div>
SVG
Fiddle example
Solution found by Jbutler438
using a clip-path and image tag in svg you can easily cut out an arrow shape at the front.
<?xml version="1.0" ?>
<svg width="300px" height="300px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="myClip">
<path d="M30 0, 100 0, 100 100, 30 100 0,50Z" />
</clipPath>
</defs>
<image xlink:href="http://lorempixel.com/300/300" x="0" y="0" width="100%" height="100%" clip-path="url(#myClip)" />
</svg>
Related
My problem here is that I want to make a half in the text wherein the the other side of text is just a div with a background color and other half is an image so that I can mask it in a text.
Here is the sample code.
.title {
font-size: 75px;
display:flex;
align-items:center;
justify-content:center;
word-break: break-word;
text-align: center;
z-index: 1;
padding:10px;
}
.box {
z-index: -1;
position: absolute;
width: 50%;
height: 100%;
left: 0;
background: #83CBFF;
}
img {
object-fit: cover;
z-index: -1;
position: absolute;
width: 50%;
height: 100%;
right: 0;
}
<div class="title col-auto">
<div class="box"></div>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/The_Blue_Marble_%28remastered%29.jpg/640px-The_Blue_Marble_%28remastered%29.jpg" alt="">
EARTH
</div>
As far as I know I can only do something like this let say..one
.title {
font-size: 75px;
display:flex;
align-items:center;
justify-content:center;
word-break: break-word;
text-align: center;
z-index: 1;
padding:10px;
background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/The_Blue_Marble_%28remastered%29.jpg/640px-The_Blue_Marble_%28remastered%29.jpg') 50% 50%/cover;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
<div class="title col-auto">
EARTH
</div>
But I want to cut it in both sides so it looks incredible...(In other thoughts I'm thinking about using SVG about it but still doesn't make it till work since it is not -webkit-transparent to the text. So anyone can help me? Thanks for any response.
Also I'm thinking maybe it can be done by something like this in .title
background: url(./images/earth.png),linear-gradient(to top,red ,blue ) 50% 50%/cover;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
but it will not workout that way.
If I understand you correct, an example in SVG could look like this. Here I use the text as a mask on the image and rectangle.
<svg xmlns="http//www.w3.org/2000/svg" viewBox="0 0 100 20">
<defs>
<mask id="text">
<text x="50" y="10" text-anchor="middle"
dominant-baseline="middle" font-size="18"
font-weight="bold" fill="white">EARTH</text>
</mask>
</defs>
<g mask="url(#text)">
<image x="40" y="-20" width="60" height="50"
href="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/The_Blue_Marble_%28remastered%29.jpg/640px-The_Blue_Marble_%28remastered%29.jpg"/>
<rect width="50" height="20" fill="#83CBFF"/>
</g>
</svg>
I'm trying to use an inline svg as a background-image (data url), but background-size:cover does not seems to work well, however with 200% its fits perfectly.
I'd like to understand the issue so that I can make sure I have a consistent render with different browsers. Ideally I'd like to keep using background-size:cover if possible
.card {
display: inline-flex; /* required in my context */
width: 45vmax;
background-color: lightblue;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 2 2"><g fill="red"><path d="M0,1v-1h1z" opacity=".4" /><path d="M1,0v1h-1z" opacity=".2"/></g></svg>');
background-size: cover;
}
._200percent {
background-size: 200%;
}
.video {
padding-top: 56.25%;
}
body {
margin: 0;
}
<a class="card">
<div class="video"></div>
</a>
<a class="card _200percent">
<div class="video"></div>
</a>
As commented you can adjust the viewbox to cover the path and avoid having extra spaces and the use of 200%
svg {
border:1px solid;
width:200px;
}
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 2 2"><g fill="red"><path d="M0,1v-1h1z" opacity=".4" /><path d="M1,0v1h-1z" opacity=".2"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 1 1"><g fill="red"><path d="M0,1v-1h1z" opacity=".4" /><path d="M1,0v1h-1z" opacity=".2"/></g></svg>
You can also do this using CSS and gradient without the need of SVG
.card {
display: inline-flex; /* required in my context */
width: 45vmax;
background:
linear-gradient(to bottom right,rgba(255,0,0,0.4) 49.5%,rgba(255,0,0,0.2) 50%),
lightblue;
}
.video {
padding-top: 56.25%;
}
body {
margin: 0;
}
<a class="card">
<div class="video"></div>
</a>
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've looked a bit for this, and can't find it on SO. I basically have to put together a design as follows...
I'm using Bootstrap4 and I'm pretty good at CSS, and this is still giving me a bit of an issue. It should be full-screen, but I'd be open to giving it a max-width of 1400px.
My Idea: I currently had the idea of doing an element with the two elements on the side, and then use transform to rotate, and then put another element inside that has the background image and then uses transform to un-rotate it.
Here's my codepen to show what I've tried -
https://codepen.io/Gwapedwink/pen/ZaPvyo?editors=1100
<section>
<div class="image-splitter">
<div class="inner left">
<span style="background-image:url('http://placehold.it/940x240/222222/fff&text=left"></span>
</div>
<div class="inner right">
<span style="background-image:url('http://placehold.it/940x240/b4d455/fff&text=right"></span>
</div>
</div>
</section>
with the CSS of:
.image-splitter {
background-color: #f1f1f1;
position: relative; /* allows for absolute inside */
height: 240px; /* height of this section is 240px */
width: 100%;
overflow: hidden; /* important for this idea */
}
.image-splitter .inner {
position: absolute;
width:1000px;
height: 500px;
top:50%;
transform:translateY(-50%);
-webkit-transform:translateY(-50%);
overflow:hidden;
}
.image-splitter .inner.left {
right: 50%;
transform: rotate(5deg) translateY(-50%);
z-index:10;
}
.image-splitter .inner.right {
left: 50%;
z-index:9;
}
.image-splitter .inner span {
display: block;
position: absolute;
top: 50%;
height:350px;
width: 100%;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
/* background image properties */
background-position: center;
background-size: cover;
}
.image-splitter .inner.left span {
right: -50px;
transform: rotate(-5deg) translateY(-50%);
-webkit-transform: rotate(-5deg) translateY(-50%);
}
.image-splitter .inner.right span {
left: 0;
}
You can see that there are so many issues already, and I want to know if I'm missing something that's available. You can see that the text in the codepen should be level, but it isn't.
Thanks in advance!
I think the vertical misalignment of the texts on the two sides is due to how placehold.it renders the text onto the images, and not related to your code. If you observe the two sample images –"left" and "right"– alone, you will notice that the baseline of the printed texts are already on different heights. Here is an overlay of the two images in question:
This thing happens because of the two strings have different overall heights. So by using texts of similar heights could solve your issue with the placeholder images. I would use square brackets to equalise the height like so: "[left]" and "[right]".
To demonstrate this, I used another approach to achieve your splitted image effect. This one makes use of the clip-path css property, which is not available on IE and Edge, so it might or might not work for you in production. On the other hand, this one simplifies the css big time.
body { margin: 0; }
.image-splitter {
position: relative;
}
.inner {
overflow: hidden;
width: 60%;
height: 240px;
background: transparent no-repeat center center / cover;
}
.right {
position: absolute;
top: 0;
right: 0;
-webkit-clip-path: polygon(calc(16.667% + 50px) 0%, 100% 0%, 100% 100%, 16.667% 100%);
clip-path: polygon(calc(16.667% + 50px) 0%, 100% 0%, 100% 100%, 16.667% 100%);
}
<section>
<div class="image-splitter">
<div class="inner left" style="background-image:url('http://placehold.it/940x240/222222/fff&text=[left]">
</div>
<div class="inner right" style="background-image:url('http://placehold.it/940x240/b4d455/fff&text=[right]">
</div>
</div>
</section>
After recommendations from #dference, it became clear to use clip-path. The issue is of browser compatibility.
So, I took his answer above and then converted it to using SVG - but also had to use foreignObject to allow Edge support.
<div class="image-splitter">
<div class="inner left" style="background-image:url('https://picsum.photos/760/240?image=1067"></div>
<div class="inner right">
<!-- SVG CLIP -->
<svg width="100%" height="240px">
<defs>
<clipPath id="clipPolygon">
<polygon points="0 240,2000 240,2000 0,50 0">
</polygon>
</clipPath>
</defs>
<foreignObject clip-path="url(#clipPolygon)" width="100%" height="100%">
<img src="https://picsum.photos/760/240?image=1049">
</foreignObject>
</svg>
<!-- / SVG CLIP -->
</div> <!-- /.inner.right -->
</div> <!-- /.image-splitter -->
Throw that in with the CSS
/* image splitter */
.image-splitter {
position: relative;
height: 230px;
overflow: hidden;
& .inner {
overflow: hidden;
width: 60%;
height: 240px;
background: transparent no-repeat center center / cover;
}
& .right {
position: absolute;
top: 0;
width:50%;
right: 0;
/*-webkit-clip-path: polygon(calc(16.667% + 50px) 0%, 100% 0%, 100% 100%, 16.667% 100%);
clip-path: polygon(calc(16.667% + 50px) 0%, 100% 0%, 100% 100%, 16.667% 100%);*/
/* Clipping */
-webkit-clip-path: polygon(0px 240px,2000px 240px,2000px 0px,50px 0px);
clip-path: url("#clipPolygon");
& img {
width: 100%;
min-height: 240px;
}
}
}
You can take a look at this at my codepen - and you can also see the neat way that I did the content splitter below also.
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.