52 Car crusher drop shadows

Posted: Feb 27, 2006, under Graphical design, DHTML. Updated: Aug 21, 2006. Add a comment!

1. Introduction

You all know how to create rounded corners with nothing but 4 DIV's and 4 images, in pure CSS and HTML, without any tables or 1px GIF’s.

Let’s start from the rounded corners technique and see if we can build an all-around shadow which can adapt to whatever size the inside container takes.

Car Crusher

I’m not going to insist on classical drop shadows, which you probably read all about on A List Apart. That’s because those techniques and this one don’t have that much in common apart from the idea of using positioned backgrounds.

We’re trying to surround a box (id main) with an all-around drop shadow. We accomplish this by starting with a big box and progressively “crushing” other boxes inside it, as well as applying shadows to the edges as we go. Hence the car crusher nickname.

2. Demo

For the impatient ones, here’s the demo and the stylesheet.

Yes, yes, I didn’t make up for Internet Explorer’s lack of PNG transparency. It still works, even if it looks ugly. I’m not familiar with any technique that allows for PNG transparent backgrounds for boxes of variable size to work on Explorer 6-.

You should also read the “Complications” section below to understand why I did some things the way I did.

3. Explanation

First, you have to create eight slices of shadow, which will be used to form the entire big one. You need four rectangles for the corners, two 1px wide slices for the top and bottom shadows, plus two 1px high slices for the left and right shadows. You can produce a full shadow in many graphical editor applications, then you can slice what you need from there. Check that they align properly.

We’re going to arrange the eight shadows in eight places around the main box, which we’ll call using cardinal directions: NW, N, NE, E, SE, S, SW and W, for easier orientation.

First, we replicate the rounded corners technique exactly. We position each corner shadow in its own box, non-repeatable, and we overlap the four DIV's perfectly.

The innermost corner box (let’s say SW) will use a left and right padding to compress all the inner boxes. We do this so that the top and bottom shadows in the immediately following boxes won’t overlap the corners.

Then we have to place the North and South backgrounds at the top and bottom of the N and S boxes, respectively, with repeat-x. At the same time we apply top and bottom padding on the S box (or on the N box, doesn’t matter), in order to once more crush the inner boxes.

For the side shadows we pull a very similar trick, but now we’ll switch to using left and right margins. Negative margins, even. The reason is that up until now we’ve crushed the boxes, but we went too far. For the last two shadows (the sides) we need to expand back, sideways, to align ourselves with the corners again. So, apply negative left and right margins to the E or W box, as well as the respective shadows as backgrounds, with repeat-y.

Finally, we’re down to the last box, which has the actual content. But before we can fill it, we need to crush it sideways once more, using (positive) margins, because otherwise it would be left expanded.

4. Complications

There are some things that stop this technique from being perfect, for the time being. It all goes well until the inner-most box, where very weird things start happening.

4.1. Content margins compose with the borders

If we have any elements with non-null margins, inside the main box ie. in the actual semantic content, then their margins go outside the main box and compose with the top and bottom shadow boxes. They cause them to grow, thus breaking the continuous shadow effect.

However, if we only enter inline elements in the main box, or we take care not to have margins for the top-most and bottom-most element, all works well.

Here’s an example using an <h1> and a <p> to show you what I mean: Example #1. As you can see, the corners and the top/bottom sides have grown vertically instead of keeping their height and letting the inner-most box grow instead.

Step two on the road to Weird-Town: the moment we apply a border or a padding on the inner-most box, browsers such as Mozilla [Firefox] or Opera suddenly fix the margin composition problem. I have no idea why and it seems silly to me, but then again I’m not a CSS guru.

So, if you have a border or padding, things happen as you’d expect them to: even if inner elements have margins, they cause the box to grow, they don’t cause spacing from the other outer boxes. Here’s how it looks now: Example #2.

So, to work around this problem, either make sure than the contents doesn’t have margins, or apply borders and/or padding to the content area. There are various ways to tackle the margin issue:

  • You can make sure that the first <h1> and the last <p> have top margin and bottom margin, respectivelly, equal to zero.
  • You can wrap the content inside yet another block element and make sure it doesn’t have margins.

4.2. Explorer: box background runs over the containing box’s padding

Looking good so far, right? Wrong: here comes Internet Explorer. It also fixes the problem, but introduces a new one: now the W/E boxes (used for side shadows, magenta in the example) suddenly decide to ignore the padding-bottom imposed by the containing vertical shadow boxes (S, or red in the example) and take over the entire bottom of the shadow.

In other words, in IE, the content of the box respects the padding of the enclosing box and stops, but its background runs on! I suppose this behaviour is subject to some debate, so I won’t insist upon it. What we want is a solution.

This second problem can be “solved” by using the inner-most box (#main) for the final compression exclusively (margins on left and right), and inside it we put a :shock: <table> which we style to our heart’s content: paddings, borders, backgrounds, whatever. Here’s how it looks when using a <table>: Example #3.

Tip: If you’re using a fixed-width design (which I’m not) you can use another workaround, which doesn’t use tables. Simply apply display:inline-block to the inner-most <div>. It also happens to be supported cross-browser.

I’m really sorry for having to resort to such tricks and even tables. But even if it bothers you, please keep in mind that we’re using a whole bunch of non-semantic <div>’s anyway. I think we’re already well past the spirit of clean CSS. Adding a table won’t make much difference.