CSS rendering bug on rotated :after pseudo elements - css

I sure hope I did not miss the according discussion on this:
I define an ":after"-pseudo element to create a triangle on top of a <p> (<div> looks the same) using the following CSS-code:
div {
padding-top: 20px;
}
p {
position: relative;
width: 200px;
height: 100px;
display: block;
background-color: #ccc;
margin: 75px auto 0 auto;
}
p:after {
content: "";
position: absolute;
top: -35px;
width: 50px;
height: 50px;
left: 15%;
background: #ccc;
border: 10px solid #fff;
border-color: #fff #fff #ccc #ccc;
transform: rotate(-45deg);
clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.dark {
background-color: #333;
}
.dark p:after {
border: 10px solid #333;
border-color: #333 #333 #ccc #ccc;
}
<div>
<p></p>
</div>
<div class="dark">
<p></p>
</div>
I noticed a thin outer visible edge (seemingly in the given background-color) in Safari, chromium forked Browsers (Chromium, Vivaldi, Brave, Chrome, Edge) and even worse in Firefox (all on Mac OS X 10.14). I took screenshots in different colors (and magnified by 2) to illustrate the issue. All of this on a non-retina 1080p display.
Can anyone explain this to me?
(The clip-path prevents the edge (which surrounds the whole pseudo-element) from reaching down inside the parenting div.)

The issue seems to be related to angles, Specifically any angle that is not a straight angle.
Related issue
In gradients it happens when there's hard stops from one color to the next, the fix is to avoid hard stops and give the colors room to transition so we don't see the pixels.
div {
height: 300px;
width: 300px;
display: inline-block;
}
[problem] {
background: linear-gradient(30deg, black 50%, orange 50%) no-repeat;
}
[solution] {
background: linear-gradient(30deg, black 49%, orange 50%) no-repeat;
}
<div problem></div>
<div solution></div>
Your issue is similar when you rotate the element to an angle that is not straight the edges are jagged We can't use the same idea from gradients Unfortunately.
*,
*:after,
*:before {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div {
background-color: #333;
height: 300px;
width: 300px;
position: relative;
}
div:after {
content: "";
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
background: #ccc;
transform: rotate(1deg);/* any angle that is not right*/
border: 30px solid #333;
}
<div></div>
Though we can clip it, to prevent the hard stop between the :after's background and the background of the parent because they're different.
*,
*:after,
*:before {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div {
background-color: #333;
height: 300px;
width: 300px;
position: relative;
}
div:after {
content: "";
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
background: #ccc;
background-clip: content-box;
transform: rotate(-45deg);
clip-path: polygon(0 0, 100% 0, 100% 100%);
border: 30px solid #333;
}
<div></div>
Original code:
html, body {
margin: 0;
padding: 0;
}
div {
margin: 0;
padding: 20px;
}
p {
position: relative;
width: 200px;
height: 200px;
display: block;
background-color: #ccc;
margin: 75px auto;
padding: 0;
}
p:after {
content: "";
position: absolute;
top: -35px;
width: 50px;
height: 50px;
left: 15%;
background: #ccc;
background-clip: content-box; /* NEW */
border: 10px solid #fff;
border-color: #fff #fff #ccc #ccc;
transform: rotate(-45deg);
clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.dark {
background-color: #333;
}
.dark p:after {
border: 10px solid #333;
/* border-color: #333 #333 #ccc #ccc; */
}
<div>
<p></p>
</div>
<div class="dark">
<p></p>
</div>
Tested in latest Chrome and FF on Win 8
The issue might pop back up with the background itself, You can see it in the demo below
A bit of jaggedness on the sides of the background, it's subtle but you can see it if you're looking for it
*,
*:after,
*:before {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div {
background-color: #333;
height: 300px;
width: 300px;
position: relative;
}
div:after {
content: "";
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
background: #ccc;
background-clip: content-box;
transform: rotate(1deg);
clip-path: polygon(0 0, 100% 0, 100% 100%);
border: 30px solid #333;
}
<div></div>
We can fix it by having 4 gradients on the edges which transition outward from the color of the current background to the color of whatever is behind which would be tricky to manage dynamically.

Related

How to change shape from pointing to top to point to right

I want to change the shape of the CSS from pointing to top to point to right. This is what I have now:
And this is what I want to have:
This is my current CSS:
.base {
background: red;
display: inline-block;
height: 55px;
margin-left: 20px;
margin-top: 55px;
position: relative;
width: 100px;
}
.base: before {
border-bottom: 35px solid red;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
content: "";
height: 0;
left: 0;
position: absolute;
top: -35px;
width: 0;
}
Please with below CSS
.base{
background: red;
display: inline-block;
height: 60px;
margin-left: 20px;
margin-top: 55px;
position: relative;
width: 100px;
}
.base:before {
border-left: 30px solid red;
border-top: 30px solid transparent;
border-bottom:30px solid transparent;
content: "";
height: 0;
left:100%;
position: absolute;
top:0;
width: 0;
}
It is simpler nowadays to use the CSS clip path facility rather than have to use borders and pseudo elements.
Clip path let’s you define a polygon in terms of percentages which means it is good for responsiveness, altering automatically to fit the dimensions of the element.
Here’s an example. Change the % values to suit the look you want.
.shape {
width: 200px;
height: 200px;
clip-path: polygon(0% 0%, 50% 0%, 100% 50%, 50% 100%, 0% 100%);
background: red;
}
<div class="shape"></div>
Caveat: not supported in IE

Right/left sided triangle in css

Hi i am trying to make the following:
They triangles should be about 40% in height of the container, and 50% in width, so they meet in the middle.
I have been trying to make something similar.. but unsuccessfull so far..
And looking around, i have found nothing i could use so far.
my code:
div {
height: 373px;
width: 0px;
margin: 26px;
display: inline-block;
}
.left {
border-bottom: 100px solid #ff0;
border-left: 320px solid transparent;
}
.right {
border-bottom: 100px solid #f00;
border-right: 320px solid transparent;
}
header {
border: 2px solid black;
width: 50%;
height: 500px;
}
<header>
<div class="right"></div>
<div class="left"></div>
</header>
hoping for someone smarter than me to see where i should go from here...
Use background coloration like below:
.box {
height:300px;
background:
/* Right bottom triangle*/
linear-gradient(to bottom right,transparent 49.5%,blue 50%) bottom right,
/* left bottom triangle*/
linear-gradient(to bottom left ,transparent 49.5%,red 50%) bottom left ,
yellow;
background-size:50% 40%; /* Width height*/
background-repeat:no-repeat;
}
<div class="box">
</div>
Related answer for more details: How do CSS triangles work?
Another idea with pseudo elements (that you can replace with common elements) in case you want to have different elements.
.box {
height: 300px;
background: yellow;
position: relative
}
.box:before,
.box:after {
content: "";
position: absolute;
height: 40%;
width: 50%;
bottom: 0;
}
.box:before {
left: 0;
background: linear-gradient(to bottom left, transparent 49.5%, red 50%);
}
.box:after {
right: 0;
background: linear-gradient(to bottom right, transparent 49.5%, blue 50%);
}
<div class="box">
</div>
Since you need percent values, you can use clip-path. Beware that it may not be supported fully on some browser https://caniuse.com/#feat=css-clip-path and for some you may need prefixes (e.g. -webkit-clip-path)
.wrap {
height: 200px;
width: 100%;
position: relative;
background: #333;
}
.triangle {
background: red;
clip-path: polygon(0 40%, 0% 100%, 100% 100%);
position: absolute;
left: 0;
bottom: 0;
top: 0;
width: 50%;
}
.triangle.tr-right {
left: auto;
right: 0;
clip-path: polygon(100% 40%, 0% 100%, 100% 100%);
}
<div class="wrap">
<div class="triangle tr-left"></div>
<div class="triangle tr-right"></div>
</div>
JSFiddle
Clip-path created with Clippy
* {
box-sizing: border-box;
}
.triangular-pointer-box {
display: flex;
align-items: center;
background: #161616;
padding: 20px;
padding-left: 120px;
height: 200px;
position: relative;
width: 80%;
}
.triangular-pointer-box > h3 {
color: #fff;
}
.triangular-pointer-box:after {
content: "";
width: 0;
height: 0;
border-top: 100px solid transparent;
border-bottom: 100px solid transparent;
border-left: 100px solid #161616;
position: absolute;
right: -100px;
top: 0;
}
.triangular-pointer-box:before {
content: "";
width: 0;
height: 0;
border-top: 100px solid transparent;
border-bottom: 100px solid transparent;
border-left: 100px solid #ffffff;
position: absolute;
left: 0;
top: 0;
}
<div class="triangular-pointer-box">
<h3>Title goes here</h3>
</div>

Create this shape in CSS (half pipe shape)

I've been trying to make this shape in CSS, but unfortunately could not find a way how to. It's half pipe like shaped:
The pixelated corner should be smooth (it's a zoomed in image).
Anyone knows how to create this? Or can get me on the right tracks?
You will need to use pseudo element :after for the inner block and then apply border-radius for curved corner.
div {
height: 60px;
width: 60px;
border: solid red;
position: relative;
background: red;
border-width: 0 10px 10px 0;
box-sizing: border-box;
}
div:after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-bottom-right-radius: 10px;
background: #fff;
}
<div></div>
simple
.shape {
width: 140px;
height: 200px;
background-color: gray;
}
.shape:after {
content: '';
position: absolute;
width: 100px;
height: 160px;
background-color: #fff;
border-bottom-right-radius: 50px;
}
<div class="shape"></div>
I assume that the problem here is to create the "outer" curve, on the arbitrary background. It's achievable by the trick with clipping the needed part from the thick rounded border of the pseudo-element:
div {
width: 50%;
min-height: 4em;
margin: auto;
border: #888 solid;
border-width: 0 1em 1em 0;
position: relative;
}
div::after {
content: '';
border: #888 solid;
border-width: 0 1em 1em 0;
border-radius: 0 0 100% 0;
position: absolute;
right: -1em;
bottom: -1em;
width: 1em;
height: 1em;
clip: rect(0 1em 1em 0);
}
div:hover::after {
border-color: red; /* highlight the curved part */
}
body {
/* just as a background example */
background: repeating-linear-gradient(-45deg, #ddd, #ddd 1px, transparent 1px, transparent 10px);
min-height: 90vh;
}
<div></div>
You can do this with one element and with transparency like this:
.box {
width:80px;
height:100px;
border-right:20px solid grey;
border-bottom:20px solid grey;
box-sizing:border-box;
background:
radial-gradient(farthest-side at top left,transparent 98%,grey ) bottom right/20px 20px no-repeat;
}
body {
background:pink;
}
<div class="box">
</div>

Creating a curved shadow with a color gradient

Here is a shadow that I am trying to replicate using just CSS and I just cannot work out how to do it. I have spent hours trying. I think I need to create 2 shadow elements but I'm not sure how to proceed.
The closest thing I get is with this (an abysmal attempt - I know):
.type-product:before, .type-product:after{
z-index: -1;
position: absolute;
content: "";
bottom: 25px;
left: 21px;
width: 50%;
top: 80%;
max-width:300px;
background: #777;
box-shadow: 0 35px 20px #777;
transform: rotate(-8deg);
}
.type-product:after{
transform: rotate(8deg);
right: 20px;
left: auto;
}
Most appreciative if any CSS gurus could provide any help.
NOTE: I don't think that this link covers my problem fully. It just discusses the curve - whilst I need a curve with a color-gradient...
To me that looks like something that can be achieved using a couple of elements like shown below. The shadow is actually a linear-gradient on top of which a white circle is placed. The drawback of this approach is that it would work only with a solid background (because the circle that is overlayed would need a solid color).
That just doesn't look like it could be possible using a box-shadow because the shadow itself seems like a gradient which goes from transparent or white on the left to black in the middle to transparent or white again on the right.
The output is responsive and can adapt itself to all dimensions of the parent container. Just :hover the container in the snippet to see it in action :)
.wrapper {
position: relative;
height: 200px;
width: 200px;
overflow: hidden;
}
.content {
height: 85%;
width: 100%;
border: 1px solid;
}
.wrapper:before {
position: absolute;
content: '';
bottom: 0px;
left: 0px;
height: 15%;
width: 100%;
background: linear-gradient(to right, transparent 2%, #444, transparent 98%);
}
.wrapper:after {
position: absolute;
content: '';
bottom: -186%;
/* height of before - height of after - 1% buffer for the small gap */
left: -50%;
height: 200%;
width: 200%;
border-radius: 50%;
background: white;
}
* {
box-sizing: border-box;
}
/* just for demo */
.wrapper {
transition: all 1s;
}
.wrapper:hover {
height: 300px;
width: 400px;
}
<div class='wrapper'>
<div class='content'></div>
</div>
You can do this with :before pseudo element and box-shadow
div {
width: 200px;
height: 100px;
border: 1px solid #aaa;
position: relative;
background: white;
}
div:before {
content: '';
border-radius: 50%;
height: 100%;
width: 100%;
position: absolute;
z-index: -1;
left: 0;
transform: translateY(103%);
box-shadow: 0px -54px 13px -47px #000000, -4px -45px 35px -28px #999999;
}
<div></div>
Aside from the answers, this could also be a good box shadow for your class as well. (This is just preference & similar to what you want).
.box {
width: 70%;
height: 200px;
background: #FFF;
margin: 40px auto;
}
.type-product {
position: relative;
}
.type-product:before {
z-index: -1;
position: absolute;
content: "";
bottom: 17px;
left: 10px;
width: 50%;
top: 70%;
max-width: 300px;
background: #777;
box-shadow: 0 18px 20px #777;
transform: rotate(-8deg);
}
.type-product:after {
z-index: -1;
position: absolute;
content: "";
bottom: 17px;
right: 10px;
width: 50%;
top: 80%;
max-width: 300px;
background: #777;
box-shadow: 0 18px 20px #777;
transform: rotate(8deg);
}
<div class="type-product box">
</div>
Hope you like it.

Responsive CSS triangle with percents width

The code below will create an arrow right below an <a> element:
JSFiddle
.btn {
position: relative;
display: inline-block;
width: 100px;
height: 50px;
text-align: center;
color: white;
background: gray;
line-height: 50px;
text-decoration: none;
}
.btn:after {
content: "";
position: absolute;
bottom: -10px;
left: 0;
width: 0;
height: 0;
border-width: 10px 50px 0 50px;
border-style: solid;
border-color: gray transparent transparent transparent;
}
Hello!
The problem is that we have to indicate the link width to get an arrow of a proper size because we cannot indicate the border width in pixels.
How to make a responsive triangle percent based?
You could use a skewed and rotated pseudo element to create a responsive triangle under the link :
DEMO (resize the result window to see how it reacts)
The triangle maintains it's aspect ratio with the padding-bottom property.
If you want the shape to adapt it's size according to it's content, you can remove the width on the .btn class
.btn {
position: relative;
display: inline-block;
height: 50px; width: 50%;
text-align: center;
color: white;
background: gray;
line-height: 50px;
text-decoration: none;
padding-bottom: 15%;
background-clip: content-box;
overflow: hidden;
}
.btn:after {
content: "";
position: absolute;
top:50px; left: 0;
background-color: inherit;
padding-bottom: 50%;
width: 57.7%;
z-index: -1;
transform-origin: 0 0;
transform: rotate(-30deg) skewX(30deg);
}
/** FOR THE DEMO **/
body {
background: url('http://i.imgur.com/qi5FGET.jpg');
background-size: cover;
}
Hello!
For more info on responsive triangles and how to make them, you can have a look at
Triangles with transform rotate (simple and fancy responsive triangles)
Another solution to this would be to use a CSS clip-path to clip a triangle out of a coloured block. No IE support however, but could be used for internal tools etc.
DEMO
Written with SCSS for ease.
.outer {
background: orange;
width: 25%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1em;
p {
margin: 0;
text-align: center;
color: #fff;
}
&:after {
content: '';
position: absolute;
top: 100%;
left: 0;
right: 0;
padding-bottom: 10%;
background: orange;
-webkit-clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
}
}
I found solution that works with any width/height. You can use two pseudo-elements with linear-gradient background, like this, (fiddle):
.btn {
position: relative;
display: inline-block;
width: 100px;
height: 50px;
text-align: center;
color: white;
background: gray;
line-height: 50px;
text-decoration: none;
}
.btn:before {
content: "";
position: absolute;
top: 100%;
right: 0;
width: 50%;
height: 10px;
background: linear-gradient(to right bottom, gray 50%, transparent 50%)
}
.btn:after {
content: "";
position: absolute;
top: 100%;
left: 0;
width: 50%;
height: 10px;
background: linear-gradient(to left bottom, gray 50%, transparent 50%)
}
A modified version of the below code can help you to achieve this
HTML
<div class="triangle-down"></div>
CSS
.triangle-down {
width: 10%;
height: 0;
padding-left:10%;
padding-top: 10%;
overflow: hidden;
}
.triangle-down:after {
content: "";
display: block;
width: 0;
height: 0;
margin-left:-500px;
margin-top:-500px;
border-left: 500px solid transparent;
border-right: 500px solid transparent;
border-top: 500px solid #4679BD;
}
For further reading on responsive triangles: CSS triangles made responsive
(archived link)
I tried the other answers and found them to be either too complex and/or unwieldy to manipulate the shape of the triangle. I decided instead to create a simple triangle shape as an svg.
The triangle height can be set to an absolute value, or as a percentage of the rectangle so it can be responsive in both directions if necessary.
html, body{
height:100%;
width:100%;
}
.outer{
width:20%;
height:25%;
background:red;
position:relative;
}
.inner{
height:100%;
width:100%;
background-color:red;
}
.triangle-down{
height:25%;
width:100%;
position:relative;
}
.triangle-down svg{
height:100%;
width:100%;
position:absolute;
top:0;
}
svg .triangle-path{
fill:red;
}
<div class="outer">
<div class="inner"></div>
<div class="triangle-down">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 2 1">
<g>
<path class="triangle-path" d="M0,0 l2,0 l-1,1 z" />
</g>
</svg>
</div>
Tested FF, Chrome, IE, Edge, mob Safari and mob Chrome
Another option would be to use background liner gradients, and flex positioning to make sure that the triangle always scales to its parent container. No matter how wide or narrow you make that container, the triangle always scales with it. Here is the fiddle:
https://jsfiddle.net/29k4ngzr/
<div class="triangle-wrapper-100">
<div class="triangle-left"></div>
<div class="triangle-right"></div>
</div>
.triangle-wrapper-100 {
width: 100%;
height: 100px;
display:flex;
flex-direction: column;
flex-wrap: wrap;
}
.triangle-right {
right: 0px;
background: linear-gradient(to right bottom, #6940B5 50%, transparent 50%);
width: 50%;
height: 100px;
}
.triangle-left {
left: 0px;
background: linear-gradient(to right bottom, #6940B5 50%, transparent 50%);
width: 50%;
height: 100px;
transform: scaleX(-1);
}
I took #Probocop's answer and come up with the following:
<style>
.btn {
background-color: orange;
color: white;
margin-bottom: 50px;
padding: 15px;
position: relative;
text-align: center;
text-decoration: none;
}
.btn:after {
background-color: inherit;
clip-path: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3Cdefs%3E%3CclipPath id="p" clipPathUnits="objectBoundingBox"%3E%3Cpolygon points="0 0, 1 0, 0.5 1" /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E#p'); /* fix for firefox (tested in version 52) */
clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
content: '';
height: 50px;
left: 0;
position: absolute;
right: 0;
top: 100%;
}
</style>
Hello!
This works in Chrome and I've added a fix for Firefox. It doesn't work in Edge, however if you decrease the height of the down arrow then it doesn't look so bad.
Please note that if you are using bootstrap you will need to either change the name or override some of the styles it applies. If you decide to rename it then you also need to add the following to the .btn style:
box-sizing: content-box;

Resources