In WebKit, why does translate3d affect drop-shadow? - css

I'm using a framework (Sencha Touch) which applies the following style to a lot of elements, probably to speed them up on mobile devices:
-webkit-transform: translate3d(0px, 0px, 0px);
Normally, this doesn't change the way the element is displayed. But I've noticed that when an element has this style, it affects the drop-shadow filters on adjacent elements. In this example (using Chrome for Mac or Safari for iOS), the top image below is next to a translate3d element, and the bottom image isn't:
Can someone explain why this is, and whether there's a way to avoid it? It only seems to happen when the element with a shadow also has a z-index. But I need to keep the z-index.
Here's the source:
<style>
.top {
position: absolute;
z-index: 1;
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 50px solid blue;
-webkit-filter: drop-shadow(5px 5px 10px rgba(0, 0, 0, 0.75));
}
.bottom {
height: 80px;
}
.translated {
-webkit-transform: translate3d(0px, 0px, 0px);
}
</style>
<div class="top"></div>
<div class="bottom translated"></div>

There are some minor problems when you mix GPU and CPU rendered elements.
(When you specify translate3d you are giving the rendering engine a good reason to use the GPU, that's why it is used)
Some time ago, you could see that in Canary, enabling and disabling GPU rendering.
With your fiddle, however, Canary displays ok in any mode. (27.0.1447.0)
The only way that you can get stable results, I think, is making most of the display thru the GPU. for instance:
.top {
position: absolute;
z-index: 1;
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 50px solid blue;
-webkit-filter: drop-shadow(5px 5px 10px rgba(0, 0, 0, 0.75));
}
.bottom {
height: 80px;
}
div {
-webkit-transform: translate3d(0px, 0px, 0px);
}
updated fiddle
crude, I know, but you get the idea.

Related

Transform pseudo-element based on parent

I'm making a Material Design web app, and I made it so that the drawer is only one element using a :before pseudo-element to make the scrim (to darken the background and bring the drawer to prominence).
#app-bar-drawer {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 350px;
max-width: 85%;
background: #fff;
z-index: 1200;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.2), 0px 16px 24px 2px rgba(0, 0, 0, 0.14), 0px 6px 30px 5px rgba(0, 0, 0, 0.12);
opacity: 0;
pointer-events: none;
transform: translateX(-110%);
transition: opacity .1s, transform .3s;
}
#app-bar-drawer.in {
opacity: 1;
pointer-events: all;
transform: translateX(0%);
}
#app-bar-drawer:before {
background: rgba(0, 0, 0, 0.6);
position: absolute;
width: 10000%;
transform: translateX(350px);
top: 0;
left: 0;
right: 0;
bottom: 0;
content: "";
z-index: 700;
}
#media screen and (max-width: 400px) {
#app-bar-drawer:before {
transform: translateX(350px); /* this is what I can't figure out */
}
}
On larger phones, tablets, desktops, etc this works fine:
As you can see, the scrim is positioned at the edge of the drawer and covers the content neatly. However, with smaller devices, it moves too far and looks like this:
In this one, the scrim is at the far right edge of the screen, as the transform: translateX(350px) is pushing it too far. I've tried changing it to use a percentage, but percentages are of the :before pseudo-element, not the parent. When I tried using a pixel measure like 200px, it would either go too far (and wouldn't cover the content) or too close (and cover the drawer).
SUMMARY
I can't use either percentages or pixel measures. I'd rather do this without JS, but if necessary I'll do it. Is this possible? It needs to be a single element.
An alternative approach would be to toggle the psueodoelement on the body when the menu is toggled. You can set it to the full width of the viewport using position: fixed and coordinates, which avoids dealing with transformations.
Ensure the z-index of the menu is higher than the psuedoelement.

3D Box Shadow effect

So I know how to do a basic box shadow with CSS3. You can see that in the top of the graphic below.
The effect I'm trying to achieve is a 3D box shadow, as shown in the bottom of the graphic below.
Any ideas on how to do this with CSS3 box shadows?
Unfortunately box shadows are effectively just flat layers. However you can apply multiple box shadows to create this effect.
.box-shadow-3d{
box-shadow: 1px 1px 0px #999,
2px 2px 0px #999,
3px 3px 0px #999,
4px 4px 0px #999,
5px 5px 0px #999,
6px 6px 0px #999;
}
you can use pseudo element for as shadow
div {
background: black;
height: 100px;
width: 100px;
position: relative;
}
div:after,
div:before {
content: '';
background: grey;
position: absolute;
}
div:after {
width: 100%;
height: 20px;
left: 10px;
bottom: 0;
transform: translatey(100%) skewx(45deg);
}
div:before {
width: 20px;
height: 100%;
right: 0;
transform: translatex(100%) skewy(45deg);
top: 10px;
}
<div></div>
Here is a real 3D shadow using perspective and pseudo-element :before.
body {
background: lightblue;
}
.foo {
position: relative;
display: inline-block;
-webkit-perspective: 1000px;
-moz-perspective: 1000px;
persepctive: 1000px;
margin: 20px;
margin-top: 50px;
}
.foo .box {
transform: rotateY(-40deg);
height: 350px;
width: 250px;
background-color: black;
}
.foo:before {
content: "";
top: -15px;
position: absolute;
width: 50px;
height: 375px;
background-color: grey;
transform: translateX(215px) translateY(2.7px) rotateY(55deg)
}
<div class="foo">
<div class="box"></div>
</div>
You can stack the horizontal/vertical offsets of several box-shadows, each slightly bigger than the previous one. The more shadows you add, the more pronounced the effect. Here is a fiddle example.
div {
background: black;
height: 100px;
width: 100px;
box-shadow: 0 01px gray,
01px 0 gray,
01px 02px gray,
02px 01px gray,
02px 03px gray,
03px 02px gray,
03px 04px gray,
04px 03px gray,
04px 05px gray,
05px 04px gray,
05px 06px gray,
06px 05px gray;
}
I had some problems with these two options, so I adapted some diagonal gradients from Lea Verou's excellent book CSS Secrets. I thought about creating a gradient inside a right and bottom border via border-image, but that property does not allow edge targeting, à la border-right-image, etc.
So, I settled on using a pseudo element with two truncated corners, which seems to work pretty well. You have to be careful to adjust the width of the gradient to be 1.414 the size of half the padding, since this would be the diagonal of a square (square root of two). Also, since that's a pseudo element, be careful of the right placement. Interested to hear what you folks think.
div {
background: #bbb;
padding: 1em 1.2em;
width: 50%;
margin: 0 auto;
color: #111;
font: 150%/1.2 Georgia, Palatino, Times, serif;
position: relative;
}
div:after {
content:" ";
position:absolute;
top:0;
left: 0;
width:100%;
height:100%;
padding: 1.42em; /* (square root of gradient position) */
background: #000; /* Fallback if not supported */
background: linear-gradient(-135deg, transparent 2em, #000 0) top right,
linear-gradient(#000, #000) padding-box bottom right,
linear-gradient(45deg, transparent 2em, #000 0) bottom left;
/*I have avoided adding -webkit-, -moz and -0 prefixs for linear-gradient. You may put them in later to be extra safe*/
background-size: 50% 50%; /* There is no reason to paint the upper left quadrant, so I didn't. */
background-repeat: no-repeat;
-webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box;
/* Many people use border-box as default these days. Unfortunately, the box cannot be sized using border-box settings with the combination of padding in ems and percentages. So this is reset to content-box, just in case. */
z-index: -1; /* To keep the shadow behind the div*/
<div>This is a short sentence to demonstrate that our little div is responsive.</div>
Here's a little implementation, inspired by #Vitorino fernandes, in stylus...
offset = 10
border = 3
.offsetbox
margin offset
padding offset
text-align center
box-shadow inset 0 0 0 unit(border,px) black
background white
display inline-block
position relative
&:after,
&:before
content ''
background black
position absolute
&:after
width 100%
height offset
transform translatey(100%) skewx(-45deg)
right (offset/2)
bottom 0
&:before
height 100%
width offset
transform: translatex(-100%) skewy(-45deg)
left 0
top (offset/2)
I added some clip paths to #Vittorino fernandes code, to avoid white space between pseudos and make it sharper.
I added some 1px adjustments to avoid bad svg rendering problems.
You can use the variable called shadow-dimension to set the shadow width and height.
I Put it on a codePen:
https://codepen.io/silviamalavasi/pen/XWqeWEq
:root {
--shadow-dimension: 20px;
--blue: #0039a6;
}
.box-container {
position: relative;
}
.box-container>div {
border: 2px solid var(--blue);
}
.box-container>div:after, .box-container>div:before {
content: '';
background-color: var(--blue);
position: absolute;
}
.box-container>div:before {
width: calc(var(--shadow-dimension) + 1px);
height: calc(100% + 100px + 1px);
left: calc(var(--shadow-dimension) * -1);
transform: skewy(-45deg);
top: calc(0.5*var(--shadow-dimension));
clip-path: polygon(0% 0%, 100% 0%, 100% calc(100% - 100px - 2px + var(--shadow-dimension)), 0% calc(100% - 100px - 2px));
}
.box-container>div:after {
width: calc(100% + 100px);
height: calc(var(--shadow-dimension) + 1px);
left: calc(-0.5*var(--shadow-dimension) - 100px);
bottom: 1px;
transform: translateY(100%) skewx(-45deg);
clip-path: polygon(100px 0%, 100% 0%, 100% 100%, calc(100px + 2px) 100%);
}

How can I cut off the corner of a container without allowing content within to overflow?

A client has requested that I create an image slider which is quadrilateral but not rectangular. The slider ends half way across a page and mustn't overlap the right side content. An example of this would be:
First Attempt
I've currently created this JSFiddle demo which uses borders to sheer off the bottom right corner. This is in two parts: #intro-bottom-left which is the foreground and #intro-bottom-left-back which is the background (and what creates the border effect along the right side.
HTML
<section id="intro">
<div id="intro-bottom">
<div id="intro-bottom-left-back"></div>
<div id="intro-bottom-left"></div>
</div>
</section>
CSS
#intro-bottom-left {
height: 0;
width: 0;
position: absolute;
top: 0;
left: 0;
border-top: 410px solid white;
border-right: 153px solid transparent;
border-left: 211px solid white;
}
#intro-bottom-left-back {
height: 0;
width: 0;
position: absolute;
top: 0;
left: 0;
border-top: 410px solid black;
border-right: 154px solid transparent;
border-left: 212px solid black;
}
I've made this on a slightly smaller scale to fit easily within the JSFiddle result UI area. This creates:
Note how I've given the body a grey background to show how this isn't affected.
The Problem
The client has specified that this must work on IE8, which rules out using the CSS3 border-image property or using CSS3 2D Transformations to add in or modify the images and their containers.
I need to somehow add in an <img /> element on top of this which is unable to overflow outside of the boundaries of the shape, without covering any of the area to the right. As the #intro-bottom-left container has no width or height other than what is created by the borders, simply adding an image in and giving the container overflow: hidden will not achieve anything.
Given the below markup (JSFiddle), what can I do to get this image to display the same as the first example at the top without overlapping any of the right content?
<div id="intro-bottom-left">
<div class="slide">
<img src="http://placehold.it/500x500" />
</div>
</div>
You could use IE filters to achieve the CSS3 2D Transformation effect in IE8. Try this tool:
http://www.useragentman.com/IETransformsTranslator/
Update:
The fiddle below looks the same in IE8, Chrome and Firefox:
http://jsfiddle.net/myajouri/jjbrn/
You have to adjust the positioning manually through negative margins to compensate for the difference in transform-origin.
body {
background: #444;
}
#intro-bottom {
overflow: hidden;
}
#intro-bottom-left {
width: 500px;
height: 500px;
padding-left: 90px;
margin-left: -90px;
overflow: hidden;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-transform: skew(-20deg);
-moz-transform: skew(-20deg);
-o-transform: skew(-20deg);
transform: skew(-20deg);
/* IE8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=-0.3639702342662026, M21=0, M22=1, SizingMethod='auto expand')";
margin-left: -182px\0/;
}
.slide {
width: 500px;
height: 500px;
-webkit-transform: skew(20deg);
-moz-transform: skew(20deg);
-o-transform: skew(20deg);
transform: skew(20deg);
/* IE8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0.3639702342662022, M21=0, M22=1, SizingMethod='auto expand')";
margin-left: -90px\0/;
}

draw angular side / parallelogram using CSS

Need to draw angular sides of menubar as
inner content may be the some labels or links.
How about using CSS3 transform skew?
Demo
.shape {
width: 200px;
height: 50px;
-webkit-transform: skew(30deg);
-moz-transform: skew(30deg);
transform: skew(30deg);
background: #000;
margin: 20px;
}
Nothing much to explain here, it's a simple div element, which I've skewed by 30deg which will result in the shape you expected.
Note: It's a CSS3 property, so older browsers, as well as IE will spoil your things, make sure you use CSS3 Pie.
Other way to achieve this is by using :after and :before pseudo and CSS Triangles along with content property.
Demo 2 (Kept red triangles for demo purpose)
Demo 3 (Color Changed)
Demo 4 (As you commented, you need to use top: 0; for :before and :after pseudo as well, because when you add text, it will shift both the triangles from the top. So inorder to prevent that, use top: 0;)
Here, am using a simple div element and placing 2 CSS triangles which are positioned absolute to the container. This is more compatible than above, if you are going for a NON CSS3 solution, you can choose this. Make sure you use display: block; for :before as well as :after. And ofcourse you can merge the common styles but I've kept both separate, so that you can get easability to customize them separately.
.shape {
width: 200px;
height: 50px;
background: #000;
margin: 50px;
position: relative;
}
.shape:before {
display: block;
content: "";
height: 0;
width: 0;
border: 25px solid #f00;
border-bottom: 25px solid transparent;
border-left: 25px solid transparent;
position: absolute;
left: -50px;
}
.shape:after {
display: block;
content: "";
height: 0;
width: 0;
border: 25px solid #f00;
border-top: 25px solid transparent;
border-right: 25px solid transparent;
position: absolute;
right: -50px;
}
HTML
<div class="shape">
<div class="text">
text goes here
</div>
</div>
CSS
.shape {
width: 200px;
height: 30px;
-webkit-transform: skew(30deg);
-moz-transform: skew(30deg);
transform: skew(30deg);
background: #000;
margin: 20px;
color:#fff;
}
.text{
width: 150px;
height: 30px;
margin:0px auto;
-webkit-transform: skew(-30deg);
-moz-transform: skew(-30deg);
transform: skew(-30deg);
color:#fff;
}
One major gripe I have with using triangular borders is that there is no easy way to have multiple triangles with different colours, even using javascript [because JS can't access the pseudo-elements :before and :after], the alternative being that I use 3 divs, align them properly, and give all of them the same colour, etc... Too much hassle.
The best way would be using transform: skew() for newer browsers.
But you need to keep in mind that this will transform every element inside that div as well. So the text inside your menu-bar would also come up skewed. To counter that, add a reverse-skew on the inner div like this:
.menu-container {
...
transform: skewX(30deg);
...
}
.menu-inner {
...
transform: skewX(-30deg);
...
}
Have fun experimenting... :)

Rounded arrows with CSS [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to create a triangle in CSS3 using border-radius
Are they possible to make with CSS?
Normal arrow:
.some_element:after{
content: '';
height: 0;
width: 0;
position: absolute;
top: 0;
left: 0;
border: 100px solid transparent;
border-top-color: #000;
}
( http://jsfiddle.net/W3xwE/ )
Rounded arrow (I want only the bottom side rounded):
:(
Yes, it is possible! You rotate the box, give it a border-radius and use a 45deg linear-gradient as a background.
DEMO
HTML:
<div class='arrow'></div>
CSS:
.arrow {
width: 7em;
height: 7em;
border-radius: 0 0 2em 0;
margin: 5em;
transform: rotate(45deg);
background: linear-gradient(-45deg, black 50%, transparent 50%);
}
If you want the angle of the arrow to be different, then you can also skew it.
Take into account the fact that CSS gradients are not supported by IE9 (I am not saying "or older" this time because you mention CSS3 among your tags). The solution in that case would be to use a solid background and to somehow make sure the upper part won't show, either by covering it with a preceding element, or by clipping it (see the answer Tim Medora provided).
Also, at this point there is still no support for the unprefixed syntax (although this will soon change :D ), so you will need to either manually add the prefixes -webkit-, -moz-, and -o-. (I did not add them in the demo because Dabblet uses -prefix-free which takes care of doing this.)
Here's a way to do it by placing a rotated square inside a box to control clipping. Personally, I think #Ana's solution is much cleaner.
http://jsfiddle.net/K44mE/14/
<div id="outer"><div id="inner"> </div></div>
#inner{
-ms-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-o-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
background-color:silver;
width:100px;
height:100px;
top: -70px;
left: 20px;
position:relative;
-moz-border-radius: 20px;
border-radius: 20px;
}
#outer {
position: absolute;
width: 140px;
height: 70px;
top:10px;
left:10px;
overflow: hidden;
border: 1px solid red;
}
CSS
.arrow {
width: 7em;
height: 7em;
border-radius: 0 0 2em 0;
margin: -2em 2.5em;
transform: rotate(45deg);
background: linear-gradient(-45deg, black 50%, transparent 50%);
}
HTML
<div class='arrow'></div>

Resources