I want to create an html element, e.g. a div, which is styled as follows:
semi-transparent background-color
rounded borders on all edges
left side of the div draws a straight line
right side of the div draws a skewed line
I'd like to create this in CSS only and wonder if this is possible. So far I came up with two different approaches which have their own drawbacks and are not fully sufficient. You can have a look at those in this fiddle:
https://jsfiddle.net/n4tecna3/
.one-side-skew-1,
.one-side-skew-2 {
font-size: 20px;
padding: 2%;
background-color: rgba(220, 50, 255, 0.6);
position: relative;
display: block;
border-radius: 4px;
z-index: 2;
color: #ffffff;
margin-top: 30px;
}
.one-side-skew-2 {
border-top-right-radius: 0px;
}
.one-side-skew-1:after {
height: 100%;
width: 20%;
position: absolute;
top: 0;
left: 85%;
display: inline-block;
content: "";
background-color: rgba(220, 50, 255, 0.6);
-moz-transform: skewX(-10deg);
-webkit-transform: skewX(-10deg);
-ms-transform: skewX(-10deg);
-o-transform: skewX(-10deg);
transform: skewX(-10deg);
z-index: -1;
border-radius: 4px;
}
.one-side-skew-2:after {
border-top: 1em solid rgba(220, 50, 255, 0.6);
border-left: 0.25em solid rgba(220, 50, 255, 0.6);
border-right: 0.25em solid transparent;
border-bottom: 1em solid transparent;
border-top-right-radius: 4px;
left: 100%;
display: inline-block;
position: absolute;
content: "";
top: 0;
}
.container {
width: 500px;
}
<div class="container">
<div class="one-side-skew-1">
<span class="inner-text">One Side Skew With Pseudo Element Skewed</span>
</div>
<div class="one-side-skew-2">
<span class="inner-text">One Side Skew With Pseudo Element Border</span>
</div>
</div>
Approach 1 .one-side-skew-1 uses a div element with round borders and a skewed, round-bordered pseudo element to create a one-side skewed element in sum. This works great as long as the background-color is solid. For semi-transparent backgrounds you will see an ugly color overlap where the element and its pseudo-element meet.
Approach 2 .one-side-skew2 uses a div element with a pseudo behind it that consists of borders only. It's somewhat hacky but gets close to my desired result. Still, the right does not look nearly as smooth as in the first approach.
Does someone else have a good solution for this problem in CSS only? Or will I have to use a fallback solution with a semi-transparent background-image to solve this?
You can use a pseudo element for all the background and hide the overflowing parts with the overflow property on the element.
This will prevent element and pseudo element background overlapping and allow semi transparent backgrounds:
div {
position: relative;
width: 250px;
font-size: 20px;
border-radius: 4px;
overflow: hidden;
color: #fff;
padding: 1% 2%;
}
div:before {
content: '';
position: absolute;
top: 0; right: 0;
width: 100%; height: 100%;
background: rgba(220, 50, 255, 0.6);
-webkit-transform-origin:100% 0;
-ms-transform-origin:100% 0;
transform-origin: 100% 0;
-webkit-transform: skewX(-10deg);
-ms-transform: skewX(-10deg);
transform: skewX(-10deg);
border-radius: 4px 4px 6px;
z-index: -1;
}
/** FOR THE DEMO **/body {background: url('http://lorempixel.com/output/people-q-g-640-480-3.jpg');background-size: cover;}
<div>content</div>
Related
I have a simple div element that I wanna apply a background shape to it when the user hovers over it by using the ::before and ::after pseudo elements. I rotated these elements with rotateX(). How can I style it that the elements shouldn't overlap each other (or at least not ruin the background color) but it should look like a single shape?
Tried using % but didn't work.
Please help.
Thanks so much 🙏
div{
width:200px;
padding: 18px;
margin: 10px auto;
/* border: 1px solid black; */
text-align:center;
position: relative;
perspective: 100px;
z-index: 1;
}
div:hover{
color:#fff;
}
div:hover::before, div:hover::after{
content: "";
display: block;
background-color: #00000050;
width: 100%;
height: 50px;
position: absolute;
left: 0%;
z-index: -1;
}
div::before{
top:0;
transform: rotateX(-75deg);
}
div::after{
bottom:0;
transform: rotateX(75deg);
}
<div>Hello World</div>
If you make half of each pseudo element only have the color then when you rotate them the colors don't overlap.
A minor adjustment to the padding of the div was needed to get the two rotated 'halves' to meet correctly so this would have to be looked at if you ever went for a responsive rather than a fixed px unit solution.
This snippet removes the background-color from the pseudo elements, instead using a linear-gradient background-image going just half way up (or down) the pseudo element.
div {
width: 200px;
padding: 18px;
padding: 16px;
margin: 10px auto;
/* border: 1px solid black; */
text-align: center;
position: relative;
perspective: 100px;
z-index: 1;
}
div:hover {
color: #fff;
}
div:hover::before,
div:hover::after {
content: "";
display: block;
margin: 0;
padding: 0;
width: 100%;
height: 50px;
position: absolute;
left: 0%;
z-index: -1;
}
div::before {
top: 0;
transform: rotateX(-75deg);
background-image: linear-gradient(#00000050 0 50%, transparent 50% 100%);
}
div:hover::after {
bottom: 0;
transform: rotateX(75deg);
background-color: transparent;
background-image: linear-gradient(to top, #00000050 0 50%, transparent 50% 100%);
}
<div>Hello World</div>
A Haworth's answer covers using linear gradient stops to hide the color of half of each pseudo element.
Another approach you could take is to use only one of the pseudo elements with a polygon clip path to make your shape.
.container { display: flex; }
.hoverable { position: relative; margin: auto; padding: 10px 100px; }
.hoverable:hover::before {
content: "";
position: absolute;
inset: 0;
/* top: 0; left: 0; right: 0; bottom: 0; */
background-color: rgba(0, 0, 0, 0.25);
clip-path: polygon(0 0, 100% 0, 80% 50%, 100% 100%, 0 100%, 20% 50%);
}
<div class="container">
<div class="hoverable">Hello, World!</div>
</div>
Ok, so in HTML I have one div tag with a before and after pseudo elements. This is my CSS:
.divClass{
background: #41423D;
height:30px;
}
.divClass:before{
content: '';
line-height: 0;
font-size: 0;
width: 0;
height: 0;
border-width :15px 7px 15px 7px;
border-color: transparent #41423D #41423D transparent;
border-style:solid;
position: absolute;
top: 0;
left: -14px;
}
.divClass:after{
content: '';
line-height: 0;
font-size: 0;
width: 0;
height: 0;
border-width :15px 7px 15px 7px;
border-color: transparent transparent #41423D #41423D;
border-style:solid;
position: absolute;
top: 0;
right: -14px;
}
So, in design it becomes like this:
___
/ \
Now all I need is a shadow on the before and after pseudo elements which are the 2 triangles on either side of the div. The pseudo elements have 0 width and height so using box-shadow is a little tricky.
I need the shadow along the triangle. So, what can I do?
You can use unicode character : ▲ to make the triangles.
Apply a text shadow on it.
If the shape of the triangle is not what you want you can adjust it with transform: rotate(); or transform: skewX(); or both.
It's a bit tricky and not perfect but it can works :
span {
display: inline-block;
font-size: 70px;
transform: skewX(29.5deg);
color: red;
text-shadow: 2px 2px 4px gray;
}
<span>▲</span>
There are some other possibilities, all describe on a CSS Tricks post, so check it if you want :
https://css-tricks.com/triangle-with-shadow/
If think you can check filter: drop-shadow() too. In case you do not need a support for all the browsers it may works for you...
edit:
According to the css tricks post, can't you do that ?
.triangle-with-shadow {
transform: rotate(-45deg);
width: 100px;
height: 100px;
position: relative;
overflow: hidden;
box-shadow: 0 20px 10px -17px rgba(0, 0, 0, 0.5);
}
.triangle-with-shadow:after {
content: "";
position: absolute;
width: 50px;
height: 50px;
background: #999;
transform: rotate(45deg); /* Prefixes... */
top: 75px;
left: 25px;
box-shadow: -1px -1px 10px -2px rgba(0, 0, 0, 0.5);
}
<div class="triangle-with-shadow"></div>
Another possibility if you just want the shape that you describe is to use the perspective :
.shape {
background: #41423D;
width: 150px;
height: 150px;
margin: 20px auto;
transform-origin:50% 100%;
transform:perspective(100px) rotateX(30deg);
box-shadow: 2px 2px 15px #41423D;
}
<div class="shape"></div>
I'm trying to create a partial shadow on a skewed div, as close as I can get to this creative.
Right now I've been trying to do this with pseudo elements (before specifically) but I found that those elements behave strangely whenever I skew the element they are applied to. The pseudo element keeps appearing on top of my div, even though the z-index is set to -1. No matter what I do with z-index, it will stay on top of it. I want it to be behind the div it's applied to, and in front of the div below, like in the creative.
Here's my ::before code and a link to the codepen
CSS
/*! Shadows */
#test-title {
position: relative;
}
#test-title:before {
z-index: -1;
position: absolute;
content: "";
bottom: 15px;
left: 10px;
width: 50%;
top: 80%;
max-width:300px;
box-shadow: 0 15px 10px #777;
-webkit-transform: rotate(-3deg);
-moz-transform: rotate(-3deg);
-o-transform: rotate(-3deg);
-ms-transform: rotate(-3deg);
transform: rotate(-3deg);
}
http://codepen.io/kathryncrawford/pen/WwWEma
Skew the parent then unskew the child at the same degree.
* {
box-sizing: border-box
}
body {
padding: 40px 0
}
section {
width: 60vw;
clear: both;
overflow: hidden;
min-height: 100px;
margin: 0 auto;
position: relative;
background: #035076
}
section article {
width: 60%;
padding: 20px;
color: white;
margin: 0 auto
}
section:nth-child(even) {
transform: skew(-45deg) translate(5vw);
box-shadow: inset 0 0 2px 0 black;
}
section:nth-child(odd) {
transform: skew(45deg);
}
section:nth-child(even) article {
transform: skew(45deg) translate(5vw);
}
section:nth-child(odd) article {
transform: skew(-45deg);
}
section:before,
section:after {
content: '';
position: absolute;
}
section:nth-child(even):before {
width: 100%;
height: 0;
bottom: 100%;
left: 0;
z-index: 6;
opacity: 1;
transform: rotate(-10deg) translateY(-30px);
box-shadow: 0px 0px 64px 30px rgba(0, 0, 0, 0.8);
}
section:nth-child(odd):not(:first-child):before {
width: 100%;
height: 0;
bottom: 100%;
right: 0;
z-index: 6;
opacity: 1;
transform: rotate(10deg) translateY(-30px);
box-shadow: 0px 0px 64px 30px rgba(0, 0, 0, 0.8);
}
<section>
<article>What our clients say About Us</article>
</section>
<section>
<article>Read More!</article>
</section>
<section>
<article>Read More!</article>
</section>
<section>
<article>Read More!</article>
</section>
The easier approach would be to put the drop shadow at the top of each box after the first. This will solve all sorts of z-index issues, since each box sits 1 level higher than the box above it.. and it allows the shadow to sit inside the container instead of outside of it.
I've also changed your shadow styling to use a radial gradient* instead of a box shadow, as it is a bit easier to control in this situation, and is also closer to your design. I also did a bit of positioning to make it look a bit better too, and get the separate sides for skew1 and skew2
I've changed your last ruleset to this:
.test-info:before {
position: absolute;
content: "";
width: 100%;
left: 0;
top: 0;
height: 30px;
}
.test-info.skew1:before {
background: radial-gradient(ellipse farthest-side at 30% top, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 100%);
}
.test-info.skew2:before {
background: radial-gradient(ellipse farthest-side at 70% top, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 100%);
}
See Demo
* note: You may want to check/add additional browser support on the gradient that I put in before using it.
I have tried, it's not perfect, but, it is closer to desired look, imho:
<div id="test-title">
<h3>What our clients say about us</h3>
</div>
<div id="shadow1"></div>
So, i've added new html element(shadow), rather than using pseudo-elements... Now, i've set z-indexes and positions properly, to hide rotated shadow div behind first div, and added this css:
#shadow1 {
position:absolute;
width:50%;
height:150px;
background:black;
top:50px;
left:11%;
z-index:6;
opacity:1;
transform: rotate(-5deg);
box-shadow: 15px 56px 50px -12px rgba(0,0,0,0.80);
}
Demo: http://codepen.io/anon/pen/vGwNqY
You can play with rotation, box-shadow, position, height... but, this could be a good start (maybe). P.S. Similar logic could be applied to second div.
try to make box shadow for the second element using :before pseudo https://jsfiddle.net/0andpzsp/
.cont {
width: 1000px;
height: 500px;
}
div[class^="d"] {
width: 70%;
height: 50%;
position: relative;
margin-left: 40px;
margin-top: 50px;
}
.d0 {
background: linear-gradient(to right, #005f8a 0%,#00486c 50%,#003a59 100%);;
transform: skew(20deg)
}
.d1 {
background: linear-gradient(to right, #005f8a 0%,#00486c 50%,#003a59 100%);;
overflow: hidden;
top: -50px;
left: 20%;
transform: skewX(-20deg);
z-index: -1
}
.d1:before {
content: '';
border-radius: 30%;
position: absolute;
display: block;
width: 600px;
height: 70px;
z-index: 9999;
top: -100px;
left: -70px;
box-shadow: -50px 60px 90px 0px #000;
transform: rotate(-5deg)
}
<div class="cont">
<div class="d0"></div>
<div class="d1">
</div>
</div>
I want to implement a picture shadow as below
I tried to use the following code, but that can't work as I want
Code snippet:
.oval {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
width: 30px;
height: 5px;
border: none;
-webkit-border-radius: 50%;
border-radius: 50%;
color: rgba(0, 0, 0, 1);
-o-text-overflow: clip;
text-overflow: clip;
background: #1abc9c;
-webkit-box-shadow: 0 100px 4px -2px rgba(15, 13, 13, 0.53);
box-shadow: 0 100px 4px -2px rgba(15, 13, 13, 0.53);
-webkit-transform: scaleX(5);
transform: scaleX(5);
-webkit-transform-origin: 0 50% 0;
transform-origin: 0 50% 0;
}
<div class="oval"></div>
I want to put the HTML code below the picture if the CSS code works well.
Another method to achieve this would be to make use of a pseudo-element with CSS transform. In the below snippet, the :after pseudo-element is rotated in X-axis by close to 90deg (but not by equal to 90deg) to give it an oval like appearance. Then by adding a radial-gradient background and box-shadow, we can get an appearance close to the image in the picture.
One advantage of this approach is that the shadow that is produced is responsive and so it can adapt to change in container/image sizes.
.oval{
position: relative;
height: 200px;
width: 200px;
border: 8px solid red;
border-radius: 50%;
}
.oval img{
border-radius: 50%;
}
.oval:after{
position: absolute;
content: '';
height: 100%;
width: calc(100% - 40px); /* to offset for the shadow */
top: 25%;
left: 20px; /* to offset for the shadow spread */
border-radius: 50%;
backface-visibility: hidden;
transform-origin: 50% bottom;
transform: rotateX(85deg);
background: radial-gradient(ellipse at center, rgba(216,216,216, 0.5), rgba(248,248,248,0.1));
box-shadow: 0px 0px 20px 20px rgba(248,248,248,0.5);
}
/* Just for demo */
.oval#oval2{ height: 300px; width: 300px; }
div{ float: left; }
<div class="oval">
<img src='http://lorempixel.com/200/200/nature/1' />
</div>
<div class="oval" id="oval2">
<img src='http://lorempixel.com/300/300/nature/1' />
</div>
You could achieve this quite easily if you're able to wrap the <img /> element in a container tag such as a <div>. By using the :after pseudo-selector on the parent div, you can achieve a similar approach using box-shadow.
For example, assuming you have the following markup:
You may add the following CSS definitions:
.image-round {
border: 4px solid red;
border-radius: 50%;
}
.image-shadow {
display: inline-block;
position: relative;
}
.image-shadow:after {
display: block;
content: '';
position: absolute;
bottom: -30px;
height: 10px;
right: 5px;
left: 5px;
border-radius: 50%;
background: #ccc;
box-shadow: 0 0 10px 10px #ccc;
}
Of course, you can modify the left and right properties of the :after pseudo-element to achieve a better look.
jsFiddle Demo
I know you can use border-radius to round objects with CSS, but I am trying to figure out how to round, or possibly hide, the pointed parts of a CSS shape when overlaying over a circle:
It's tough to explain without a Codepen:
http://codepen.io/cavanflynn/pen/gpEdJo
#circle:before {
position:absolute;
left: -10px;
top: -25px;
z-index: 100;
content: "";
border-right: 35px solid white;
border-top: 25px solid transparent;
border-bottom: 25px solid transparent;
border-left: 25px solid transparent;
}
In the Codepen you can see the circle and where parts of the white are overflowing the red boundary of it. The goal is to get rid of the white points and round the white perfectly with the red OR make the section of the circle that the white overlays completely transparent (like taking a slice from a pizza).
You can achieve this using a pseudo element, and using borders on the pseudo to manipulate the shape to your liking. This uses the transparent border color on the right side, with the rest with a solid color. A demo of this is shown below:
html {
background: url(http://lorempixel.com/800/900);
}
div {
position: relative;
height: 100px;
width: 100px;
border-radius: 50%;
background: gold;
margin: 150px auto;
}
div:before {
content: "";
position: absolute;
top: -50px;
left: -50px;
height: 100%;
width: 100%;
border-radius: 50%;
border: 50px solid red;
border-right-color: transparent;
}
<div></div>
If the angle here is too big for your liking, you could duplictate the pseudo element on an :after element, and using transform:rotate(x deg); to rotate to a specific angle
A demo of this can be seen when hovering below:
html {
background: url(http://lorempixel.com/800/900);
}
div {
position: relative;
height: 100px;
width: 100px;
border-radius: 50%;
background: gold;
margin: 150px auto;
}
div:before, div:after {
content: "";
position: absolute;
top: -50px;
left: -50px;
height: 100%;
width: 100%;
border-radius: 50%;
border: 50px solid red;
border-right-color: transparent;
transition:all 0.8s;
}
div:hover:after{
transform:rotate(45deg);
}
div:hover:before{
transform:rotate(-45deg);
}
<div></div>
Why does border-radius not work for my case?
Yes, border-radius property can be used to produce circles but it can produce a proper circle only when the height and width are the same. In your original sample, the height and width of the element is determined by the thickness of the border on the ::before element and since the border on one side is thicker than the rest, it produces a rectangle instead of a square. Because of this you would only get a oval at best and not a circle.
Even if you make the border thickness the same, the curvature of the round wouldn't match with the curvature of the outer circle (parent container) because the radii and center points are both different.
I have changed the border-color of the other three borders in the below snippet to help you visualize what is going on:
body {
background-color: black;
}
#circle {
border-right: 50px solid red;
border-left: 50px solid red;
border-top: 50px solid red;
border-bottom: 50px solid red;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
position: absolute;
left: 35px;
z-index: 100;
top: 50px;
}
#circle:after {
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
position: absolute;
left: -25px;
top: -25px;
z-index: 100;
content: "";
border: 25px solid yellow;
}
#circle:before {
position: absolute;
left: -10px;
top: -25px;
z-index: 101;
content: "";
border-right: 35px solid white;
border-top: 25px solid cyan;
border-bottom: 25px solid cyan;
border-left: 25px solid cyan;
border-radius: 50%;
}
<div id="circle"></div>
One other method that is typically used to cut out the extraneous part is overflow: hidden but that also wouldn't work for your case because your outer circle is created only by border and it doesn't actually have any content height or content width. Because of this when you add overflow: hidden your pseudo elements would get totally hidden.
What are the other options that I could use?
Here is an alternate approach to create the same shape as you originally intended to with just a single element + two pseudo-elements.
The shape consists of the following components:
A container element which is converted into a circle by using border-radius: 50%.
One pseudo-element with dimensions half that of the parent and border-radius: 50% to produce the inner circle. A z-index: 1 is added to position this above the sliced area.
Another pseudo-element which has a transform: rotate(45deg) added to produce the slice.
Parent container has overflow: hidden which prevents the other parts of the pseudo-element from showing up.
Note: I assume you won't be having any concerns using transform because you had tagged CSS3.
body {
background-color: black;
}
#circle {
position: relative;
height: 200px;
width: 200px;
background: red;
border-radius: 50%;
overflow: hidden;
}
#circle:before, #circle:after {
position: absolute;
content: '';
height: 100px;
width: 100px;
}
#circle:before {
top: 25%;
left: 25%;
background: yellow;
border-radius: 50%;
z-index: 1;
}
#circle:after {
right: 0px;
background: white;
transform: rotate(45deg);
transform-origin: left bottom;
}
<div id="circle"></div>
How to get a transparent slice? (like the Colorado flag)
Alternately, if you want the slice to be transparent totally then you can use the below approach:
Here the container has a bit of padding and the background of the container is made small by restricting it to only the content-box using background-clip. This forms the inner yellow circle.
The outer red circle is made up of two pseudo-elements each of which have a background color only for half portion through linear-gradient. The other half is transparent.
The two pseudo-elements are rotated in opposing directions to make it look as though they leave a transparent cut in between.
body {
background-image: linear-gradient(to bottom, rgb(64, 64, 150) 33%, white 33%, white 66%, rgb(64, 64, 150) 66%);
background-repeat: no-repeat;
height: 260px;
}
#circle {
position: relative;
height: 100px;
width: 100px;
background: rgb(255, 243, 21);
border-radius: 50%;
padding: 50px;
margin-top: 60px;
margin-left: 50px;
background-clip: content-box;
}
#circle:after,
#circle:before {
position: absolute;
content: '';
top: 0px;
left: 0px;
height: 200px;
width: 200px;
border-radius: 50%;
z-index: -1;
}
#circle:before {
background: linear-gradient(to bottom, rgb(237, 51, 56) 50%, transparent 50%);
transform: rotate(-45deg);
}
#circle:after {
background: linear-gradient(to top, rgb(237, 51, 56) 50%, transparent 50%);
transform: rotate(45deg);
}
<div id="circle"></div>
If you don't want to use linear-gradient to produce semi-circles then you can create the same effect by using two pseudo-elements whose height is half their width and assign the appropriate border-radius to it.
body {
background-image: linear-gradient(to bottom, rgb(64, 64, 150) 33%, white 33%, white 66%, rgb(64, 64, 150) 66%);
background-repeat: no-repeat;
height: 260px;
}
#circle {
position: relative;
height: 100px;
width: 100px;
background: rgb(255, 243, 21);
border-radius: 50%;
padding: 50px;
margin-top: 60px;
margin-left: 50px;
background-clip: content-box;
}
#circle:after,
#circle:before {
position: absolute;
content: '';
top: 0px;
left: 0px;
height: 100px;
width: 200px;
background: rgb(237, 51, 56);
border-radius: 100px 100px 0px 0px;
transform-origin: 50% 100%;
z-index: -1;
}
#circle:before {
transform: rotate(-45deg);
}
#circle:after {
bottom: 0px;
transform: rotate(225deg);
}
<div id="circle"></div>
You can change the approach. Stop using :before and :after, and use overflow: hidden plus z-index layering with multiple elements.
The new HTML:
<div class="circle">
<div class="inner-circle"></div>
<div class="triangle"></div>
</div>
The CSS:
.circle {
width: 200px;
height: 200px;
background: red;
position: relative;
top: 40px;
border-radius: 50%;
overflow: hidden;
z-index: 10;
}
.inner-circle {
width: 100px;
height: 100px;
border-radius: 50%;
background: black;
top: 50px;
left: 50px;
position: relative;
z-index: 10;
}
.triangle {
position:absolute;
left: 25px;
top: 25px;
border-right: 105px solid purple;
border-top: 75px solid transparent;
border-bottom: 75px solid transparent;
border-left: 75px solid transparent;
z-index: 9;
}
Check the demo: https://jsfiddle.net/ghv02ucr/1/
I've checked on your question, try this code on your #circle:before, and you will see the triangle aligned;
#circle:before {
position:absolute;
left: -18px;
top: -25px;
z-index: 100;
content: "";
border-right: 35px solid white;
border-top: 25px solid transparent;
border-bottom: 25px solid transparent;
border-left: 25px solid transparent;
}
Or you can eliminate all the #circle:before properties to get rid of the triangle.... hope it helped.