5

I have created a shape which represents a page with a shadow that gets bigger towards the bottom.

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>

I need this to be rotated but when i try to add transform: rotate(10deg), the box-shadow illusion gets ruined and goes on top of the parent layer.

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
  transform: rotate(10deg);
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>

I have found this question: Which CSS properties create a stacking context? but there doesn't seem to be a proposed solution for my requirement.

Would there be any good solutions which would work in my case. I do not mind if they are SVG, filter, canvas or any thing else as long as it is supported reasonably well.

Stewartside
  • 20,378
  • 12
  • 60
  • 81

3 Answers3

4

If you use another div it fixes the problem that you are experiencing, so that the background colour is on the inner div and the rotate is on the outer div.

Else you might need to use another method to get the same result.

body {
  background: #dddddd;
}
.two{
background: white;
height: 300px;
width: 300px;
padding: 10px;
}
div.one {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  
  transform: rotate(10deg);
}
div.one:before,
div.one:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: rotate(1.5deg);
}
div.one:after {
  transform: rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div class="one">
<div class="two">
</div>
</div>
Andrew
  • 1,850
  • 14
  • 18
  • Your answer missed out one of the shadows on the right side. Was more hoping for a single div solution as it seems to just be a stacking issue. But thank you for the workaround/ – Stewartside Jul 24 '15 at 11:12
3

Note: This answer does not describe how to fix the stacking context problem that is seen in your approach. This just provides a couple of alternate approaches that could be used to achieve a similar effect. Advantage of these approaches is that they should work in IE10+ and does not require any extra elements.

I would still recommend vals' answer if IE support is not mandatory.

Method 1: Perspective Transform

This is almost similar to the one that you had used except that it uses a single pseudo-element rotated with perspective to produce the shadows. Since only one pseudo-element is utilized, the other pseudo can be used to add a white foreground above the shadows.

body {
  background: #dddddd;
}
div {
  position: relative;
  height: 300px;
  width: 300px;
  padding: 10px;
  margin: 40px auto;
  transform: rotate(10deg);
}
div:before,
div:after {
  position: absolute;
  content: '';
  top: 0px;
}
div:before {
  height: 100%;
  width: 100%;
  left: 0px;
  background: white;
}
div:after {
  height: 98%;
  width: 97%;
  left: 1.5%;
  transform-origin: bottom;
  transform: perspective(125px) rotateX(1deg);
  box-shadow: 10px 0px 10px rgba(0, 0, 0, .5), -10px 0px 10px rgba(0, 0, 0, .5);
  z-index: -1;
}
<div></div>

Method 2: Linear Gradients

We can use linear-gradient background images and position them appropriately to produce an effect similar to the one produced by the box-shadows. But as you can see in the output, it doesn't quite match up to a shadow because the blurred areas are not the same.

Here, we make use of the following:

  • One small angled linear gradient image (to top left) to produce the shadow on the left side of the box.
  • Another small angled linear gradient image (to top right) to produce the shadow on the right side of the box.
  • A large linear-gradient image for the white colored area (which is almost a solid color). A gradient is used here instead of a solid color because the size of a gradient image can be controlled.

body {
  background: #dddddd;
}
div {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  transform: rotate(10deg);
  backface-visibility: hidden;
  background: linear-gradient(to right, transparent 0.1%, white 0.1%), linear-gradient(to top left, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to top right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%);
  background-size: 280px 100%, 10px 97%, 10px 97%;
  background-position: 10px 0px, left top, right top;
  background-repeat: no-repeat;
  background-origin: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div></div>

The bottom of the gradient still doesn't get the blur that is seen in the box-shadow output. If needed, this can be achieved to some extent by adding even more gradients like in the below snippet.

body {
  background: #dddddd;
}
div {
  margin: 40px auto;
  height: 300px;
  width: 300px;
  transform: rotate(10deg);
  backface-visibility: hidden;
  background: linear-gradient(to right, transparent 0.1%, white 0.1%), linear-gradient(to top left, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to top right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .3) 5%, transparent 50%), linear-gradient(to bottom left, rgba(0, 0, 0, 0), rgba(0, 0, 0, .3) 5%, transparent 60%), linear-gradient(to bottom right, rgba(0, 0, 0, 0), rgba(0, 0, 0, .3) 5%, transparent 70%);
  background-size: 280px 100%, 10px 97%, 10px 97%, 10px 2.5%, 10px 2.5%;
  background-position: 10px 0px, left top, right top, left 99.25%, right 99.25%;
  background-repeat: no-repeat;
  background-origin: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div></div>
Harry
  • 87,580
  • 25
  • 202
  • 214
1

When you go into the transforms world, the transforms themselves will solve some of the problems that they create. You can solve this with the 3d equivalent of z-index, the Z coordinate

Unfortunately, this won't work in IE (I believe until they support preserve 3d)

body {
  background: #dddddd;
}
div {
  background: white;
  margin: 40px auto;
  height: 300px;
  width: 300px;
  position: relative;
  padding: 10px;
  transform: translateZ(1px) rotate(10deg);
  transform-style: preserve-3d;
}
div:before,
div:after {
  height: 96%;
  z-index: -10;
  position: absolute;
  content: "";
  left: 8px;
  top: 2%;
  width: 30%;
  max-width: 300px;
  background: transparent;
  box-shadow: -10px 0px 10px rgba(0, 0, 0, 0.5);
  transform: translateZ(-1px) rotate(1.5deg);
}
div:after {
  transform: translateZ(-1px) rotate(-1.5deg);
  right: 8px;
  left: auto;
  box-shadow: 10px 0px 10px rgba(0, 0, 0, 0.5);
}
<div></div>
vals
  • 61,425
  • 11
  • 89
  • 138
  • Excellent. I was trying the same thing yesterday without the `transform-style: preserve-3d;` (and obviously it didn't work). Even without IE support, I think this should be the accepted answer. – Harry Jul 25 '15 at 05:24
  • 1
    Strange, I was thinking that yours answer should be the accepted one :-) Thanks ! – vals Jul 25 '15 at 08:09
  • Sort of in-between. Your answer has the correct approach (but no IE support). My answer uses a different approach but better IE support. For future visitors, your answer would be more beneficial (hopefully IE will add support by then). – Harry Jul 25 '15 at 13:08
  • This also doesn't seem to be working in the latest version of chrome. It shows the same stacking issue i get with mine. – Stewartside Jul 27 '15 at 08:38