Onion Skinned Drop Shadows

A note from the editors: The techniques demonstrated in this tutorial are not for everyone. The design method works its magic by nesting divs that have no semantic or structural value. If that bothers you (and there are good reasons why it might), this is not the tutorial for you. On the other hand, if you wish to create visual effects that expand and contract to fit any object, require no image manipulation, and render the same across all browsers, then “Onion Skinned Drop Shadows” may be just what you’ve been looking for.

Onion skinned drop shadows? Yes, onion skinned. Animators use onion skinning to render what is impossible to see otherwise: a snapshot of motion across time. Now, web designers can use it to render another seeming impossibility: the truly extensible CSS-based dropshadow.

Article Continues Below

Read about onion skinning with div stacks.

Enough Already#section2

Tired of reading technique after technique outlining the many ways to skin this cat? Just consider this a demonstration of the capacity of CSS to surprise. Just when it looks as though the method is clear and the way is defined, one more wandering mind reveals another creative approach. If all you glean from this article is a deeper appreciation for the diversity of solutions CSS affords, then you’ve benefited far more than any one techniquecould deliver. Read on.

What’s different#section3

There are several differences between this technique and those offered previously, both here and elsewhere. Some may find it simpler than others, some more stable; but no matter how you look at it, it is more extensible. With just one set of simple rules, these new drop shadows:

  • Automatically expand and contract to fit any object, without specifying widths.
  • Allow you to modify the shadow depth with no image manipulation.
  • Render the same across all browsers without cutting any corners.

Peeling the onion#section4

Onion skinning refers to a technique in time-based arts of overlaying several frames in a sequence to discern the subtle differences between them. The animator is simply looking through a stack of layers to see one whole motion through a composite of its parts. What is opaque on one layer can be seen through the transparent areas on a layer above it. We can do the same thing with divs. Using CSS, we can stack divs together in concentric fashion, like the layers of an onion, to form a composite of anything we like. Today, we are making drop shadows.

Isometric view of an onion skinned drop shadow:#section5

  Demonstration image used for drop shadow illustration

Three divs, each containing a different shadow image component (illustration), overlay one another to form a composite drop shadow. The markup couldn’t be simpler.  Just wrap your object inside three divs, one inside the other, and give each a class name which corresponds to its role.  Since we’re “wrapping” the object, the classes we’ll assign for demonstration purposes are named wrap1, wrap2, and wrap3 (.wrap1 assigned to the outer, and .wrap3 the innermost div).

The object casting a shadow

The CSS is a bit more complicated — but not much. There are basically three things that the style rules must accomplish for the shadow:

  • Make it show.  Assign each of the three shadow image components (shadow + two corners) to a different layer in the onion skin stack.
  • Make it drop. Create an offset which moves the drop shadow down, and to the right of the object.
  • Make it shrink-to-fit. Force the divs surrounding the object to collapse upon it.

Step one: rendering the shadow#section6

The basic idea is to assign one of the three shadow image components to each of the divs through its corresponding class.  The sequence of which div gets which component is crucial, though.  The purpose for having three image components is to use the two small ones at the top-right and bottom-left positions to mask the clipped edges of the large shadow revealing only soft, rounded edges all around.

In order for one image to mask another, it must reside above it in the z-index. We don’t have to worry so much about indexing the stack, since the nested structure of our divs carry’s an intrinsic stacking order that works for us. We simply assign the class with the shadow to the outermost div at the bottom of the stack. Since the divs inside it will naturally sit on top, we’ll give the classes with the corner images to them.

.wrap1 {
  background-image:url(shadow.gif);
  }
.wrap2 {
  background-image:url(corner_bl.gif);
  }
.wrap3 {
  background-image:url(corner_tr.gif);
  }

Once we’ve assigned the right images to the right classes, we need to give them position — background-position, that is.  If we left the rules alone at this point, all we would see is a tiled background of “corner_tr.gif” since it’s sitting highest in the stacking order.  Remember, onion skinning requires transparency in the upper layers so that we can see what’s beneath.  To do this, we’ll cancel the repeating property of all the background images with no-repeat, and position them where they belong to form the composite of our drop shadow.

Common sense tells us that an image named “corner_tr.gif” should reside in a corner — probably the top-right; ditto for “corner_bl.gif” (bottom-left). But, what about the large shadow image? Does it even need position? Yes — more than any other, in fact. If we want our shadow to fall to the bottom-right of whatever object we put it under, we must specify that direction in our .wrap1 rule.  Otherwise, it will automatically fill the div relative to its top-left corner, the opposite of what we need.

The base layer: div.wrap1#section7

The outermost div holds the largest of the three shadow components, which is positioned right bottom.

.wrap1 {
  background:url(shadow.gif) right »
  bottom no-repeat;
  }

The middle layer: div.wrap2#section8

The second div, nested inside the first, masks the clipped lower-left corner of the shadow underneath, giving it a rounded appearance.

.wrap2 {
  background:url(corner_bl.gif) left »
  bottom no-repeat;
  }

The inner layer: div.wrap3#section9

The third div, nested inside the second, takes care of the clipped corner in the upper right.

.wrap3 {
  background:url(corner_tr.gif) right »
  top no-repeat;
  }

Step two: dropping it down#section10

The next step for the CSS is to create the offset that makes the drop shadow drop.  This could not be more simple.  All it takes is a little bit of padding on the right and bottom of the innermost div.  When the padding causes this div to expand away from the object inside it, the two outer divs expand with it.  The result: all three shadow components, positioned along the right and bottom sides of their divs move in tandem, and can now be seen through the gap created by the padding.

.wrap3 {
  padding:0 4px 4px 0;
  background:url(corner_tr.gif) right »
  top no-repeat;
  }

Modifying the offset#section11

Changing the amount offset for your shadows is almost as easy as simply changing the padding values on the .wrap3 rule. We say “almost” because adjusting the padding merely moves the shadow while the corners continue to hug the edges of their containers.  To accurately simulate a shift in offset, you’ll need to tweak the background-position of both corners relative to the padding.

Some would say that it’s good enough to simply adjust the padding, and leave it at that. No sense complicating things to achieve a nuance that is barely discernable for most people anyway. Others would argue that it cheapens the effect to cut corners this way.  If a method will support better aesthetic and technical fidelity, as designers we’re obligated to use it to its full potential. It’s likely that most, however, could go either way.

Judge for yourself.#section12

Flog logoThis image is used below to demonstrate two sets of drop shadow styles.  All instances, in both sets, draw their background-image properties from the same basic set of rules.  In other words, the exact same shadow graphics are used for every instance.  The only difference is the degree of offset.

In the first set of examples, the offset is modified by adjusting only the padding values in the .wrap3 rule, which moves only the shadow — not the corners. In the second set, both the padding and background-position values of the corners are modified so that the entire shadow, corners and all, moves as the offset changes.

 

 

 

 

 

 

Set 1. Padding Only
Flog logo
Flog logo
Flog logo
8 pixels 12 pixels 18 pixels
 

 

 

 

 

 

Set 2. Padding and Background-Position
Flog logo
Flog logo
Flog logo
8 pixels 12 pixels 18 pixels

If you can discern the difference between the two sets, and you prefer set two, then you’ll need to adjust the background-position of your corner images to compensate for the offset in padding. Further, you’ll need to add additional white pixels to the outside edges of your corner images. This allows them to move away from the boundary of the div, without losing their ability to mask the clipped edge of the shadow underneath. Each shadow style is a little different, and as you begin experimenting with offsets, your particular corner white space requirements will become clear.

Step three: making it shrink-to-fit#section13

A little sleight of hand was necessary to coax every browser that was tested into conformity with this requirement, a potential deal-breaker.  But, without this behavior, each and every instance of the shadow style would require the foreknowledge of the object’s width in order to specify the width of the shadow.  Obviously, that wasn’t going to cut it.

Most of the browsers tested would allow the divs to collapse when they received the float property. This would have sufficed, if it weren’t for those of you who use IE5 on the Mac. The mere fact that you exist was reason enough to explore alternatives. Unfortunately, none were found. At least, none that worked universally.

Some research and experimentation eventually revealed that inline-table, an obscure CSS display property value, would be the saving grace of this technique. Right, wrong, or indifferent, this was all that could be found, and it would have to do. So, using the commentedbackslash method to isolate Mac IE5, we give it display:inline-table,and all the rest receive float. So there it is, shrink-to-fit — everything, everywhere, in every browser.

The composite result:#section14

  Demonstration image used for drop shadow illustration

With all three steps in place our CSS looks like this:

.wrap1, .wrap2, .wrap3 {
  display:inline-table;
  /* */display:block;/**/
  }
.wrap1 {
  float:left;
  background:url(shadow.gif) right »
  bottom no-repeat;
  }
.wrap2 {
  background:url(corner_bl.gif) left »
  bottom no-repeat;
  }
.wrap3 {
  padding:0 4px 4px 0;
  background:url(corner_tr.gif) right »
  top no-repeat;
  }

One more rule, for good measure#section15

As in the above example, images will often be used with this technique.  For those occasions, an additional rule which sets a shadowed image’s display property to block will help keep unwanted white space from intruding on a good thing. Just add the following rule to your style sheet:

.wrap3 img {  display:block;  }

Do it yourself#section16

At this point, you may want to browse the drop shadow gallery to get a feel for what is possible with this technique.  We’ve made it easy to download a variety of shadow source images so you can get started.

About the Author

Brian Williams

As Technical Design Director for Wachovia, Brian is slowly contributing to Web Standards ubiquity in big-corporate ways. His design of Calibre.com is the company’s proud foray into standards-based design. Perhaps, someday, he’ll do something with his personal site.

129 Reader Comments

  1. This technique does destroy the sanctity of using web standards. It’s an advanced aesthetic design feature that exploits the flexibility of the markup. Doesn’t make it wrong. But it may not make it right in your individual eyes either.

    We can all keep making boxes with 3 pixel borders though. Techniques need to be exploited. They need to be pushed and some rules need to be broken. Then we can all sit back and see what we’ve done and learn from that.

    It the concept that has brought us all this far and it hasn’t failed us yet.

  2. I tried plugging this into my website on a single image and it basically failed .

    it moved the image to a higher layer and showed no dropshadow effect whatsoever.

  3. Can you make the shadow go all the way round an image. I thought of putting in 2 more div’s with the other 2 corners in then and giving the shadow a 4px fade on all sides, but the shadow doesn’t resize so if it’s possition is set to bottom right then the top and left borders don’t work.

    Any one got any great ideas to solve this or can’t it be done?

    Thanks Great scrip

  4. I’ve only read a handful of the comments, but it was enough to have me rather surprised at the amount of opposition to the very small amount* of additional markup.

    * at deviantart.com, we’ve been using 8 wrapper divs around every section area (average 10 per page), to have graphical borders surrounding these boxes, even though our primary ‘skin’ hardly uses them any more. The whole thing’s in need of a good dusting, but it’s cross-browser ambrosia, still.

    It seems to me that a lot of people are putting themselves inside a theoretical web that exists only in the specs of the W3; the semantic web, a properly refined XHTML, and a level of CSS that would afford the control simulated in this article (via ::outside and border-images(?)) are the systems needed to put together a shadow effect like this, devoid of mingling of content and presentation; and these systems do not exist: they are still on paper, they are still just ideas, and they are waiting for the fine thinkers to approve them, for the fine doers to implement them for the public, and then waiting years for everybody else to catch up with their software upgrades and finall avail of it all.

    Now I love the W3 and its specs; the sheer effort put in has produced unimaginable quality– but while the above conditions are unmet, while the core infrastructure of the standards is still being put together, and while 90% of the web’s population use a browser that does a fine job of ignoring these standards anyway, then by all means care should be taken to adhere to the standards- but for goodness’ sake, don’t drop an easy to implement div-based system because it makes the code look untidy by the standards of a few years in the future.

    As it stands, in practise, the minimal content-style separation we currently have available to us already does so quite robustly, all the way from Lynx to Opera; if it’s WCAG friendly, and it is, then it, to me, is a fine 2004-2006 solution.

  5. I recently found a tutorial at redmelon.net using this idea for creating standard corners for any box on a page.

    http://www.redmelon.net/tstme/4corners/

    If you put this together with ZACs’ javascript idea to reduce coding.
    Then use a separate style sheet for various types of corners.
    Only one class or id will be left in the code if you decide to revamp a website.

    This could be reused for an entire website

  6. This technique only uses 3 divs including the one holding the content.
    That is only 2 extra divs.

  7. well, it works perfectly!

    but how to center these containers?

    does not work. (due to float:left???).

    who can help me?

  8. Has anyone tried alpha-blended drop shadows using PNGs? I’d really like to find someone who has done this, including that filter workaround for IE.

    For the likes of Mozilla/Opera, etc, this drop shadow stuff with semi-transparent PNGs is great =)

    Only quirk: putting the divs around an existing div seems to break a lot of the layout — seemingly the float: left is the culprit =(

  9. Thanks for this helpful link, Brian. It works fine, but now there is another problem:

    Having seen your Article “shadow visible on all sides”, I tried to center these four containers – without any success 🙁

    #wrap0, #wrap1, #wrap2, #wrap3 {
    display:inline-table;
    /* */display:block;/**/
    position:relative;
    }
    #wrap0 {
    float:right; right:50%;
    background:url(shadow.gif) right bottom no-repeat;
    }
    #wrap1 {
    background:url(shadow180.gif) no-repeat;
    }
    #wrap2 {
    background:url(corner_bl.gif) -16px 100% no-repeat;
    }
    #wrap3 {
    padding:4px 6px 6px 4px;
    background:url(corner_tr.gif) 100% -16px no-repeat;
    }
    #center {
    width:1%; margin-left:50%; float: right;
    }

    (this centers the image, but it shows also an awful border on the right side.)

    tia.

  10. Smoother,
    Try this…

    http://www.ploughdeep.com/onionskin/360c.html

    Bear in mind, with both a 360 degree shadow and centering at work, you’ll begin to see issues that require additional CSS to work around.

    For instance, when centering http://www.ploughdeep.com/onionskin/centered.html, if you choose to put a shadow on a block of content instead of an image, you’ll need to declare a width for the block or else it will collapse – or stick something inside that forces it to expand.

    Additionally, like I said before http://www.ploughdeep.com/onionskin/360.html, offset values in the CSS become very sensitive to the dimensions and whitespace built into your shadow components. You’ll need patience, good problem solving skills and Photoshop to get this one right.

    These are quick fixes. Improvement is likely – please share what you find.

  11. Dear Brian,

    your version takes the same effects like mine. That is why I decided to use tables.

    This (non-css) works fine.

    Nevertheless I want to thank you for supporting me.

    greets from germany,
    smoother

  12. I’ve implemented Zac’s js with the onion skinning and everything works great – except links from a drop shadow image do not work in ie (5.x and 6). In Firefox, Mozilla and Netscape the links appear. Has anyone run into this issue?

  13. The javascript/css solution lets you add the effect without out cluttering you html and can be made to fail more gracefully than the pure css.

  14. Brian,
    Thanks for the tutorial on onion skinned dropped shadows. These things
    work great. I wanted to give something back to this subject and add extra
    functionality to these. I created a simple php function that will make
    using these drop shadows much easier. It even adds in one extra feature.
    This function automatically scales any image that is larger than the main
    shadow to the size of your main shadow.

    Here is the code:

    475) // comparing width
    $altered = true;
    if($size_info[1] > 470) // comparing height
    $altered = true;
    if($altered){
    if($size_info[0] > $size_info[1]){ // image is wider than taller
    $ratio = 475/$size_info[0];
    $width = 475;
    $height = $size_info[1]*$ratio;
    }
    elseif($size_info[1] > $size_info[0]){ // image is taller than wider
    $ratio = 470/$size_info[1];
    $height = 470;
    $width = $size_info[0]*$ratio;
    }
    else{
    $height = 470;
    $width = 475;
    }
    $width = intval($width);
    $height = intval($height);
    }
    }
    else
    $image = “swirl.jpg”; // default image
    if(!$right || !$down){
    $right = 10;
    $down = 13;
    }
    echo “n

    “;
    if($altered){
    echo ‘
    The object casting a shadow

    “;
    }
    ?>

  15. If you copy and paste the above code, please make sure to take into account that I didn’t take into consideration how this narrow column wraps my code. Look for wrapped single commented lines (//), and look for anything else that doesn’t look right.

    Sorry for the confusion.

  16. This should fix the wrapping issues for the code if you want to cut and paste.

    475)/*comparing width*/ $altered = true;
    if($size_info[1] > 470) /*comparing height*/
    $altered = true;
    if($altered){
    if($size_info[0] > $size_info[1]){ /* image is wider than taller */
    $ratio = 475/$size_info[0];
    $width = 475;
    $height = $size_info[1]*$ratio;
    }
    elseif($size_info[1] > $size_info[0]){ /* image is taller than wider */
    $ratio = 470/$size_info[1];
    $height = 470;
    $width = $size_info[0]*$ratio;
    }
    else{
    $height = 470;
    $width = 475;
    }
    $width = intval($width);
    $height = intval($height);
    }
    }
    else
    $image = “swirl.jpg”; /* default image */
    if(!$right || !$down){
    $right = 10;
    $down = 13;
    }
    echo “n

    “;
    if($altered){
    /* take out the » 2 lines down */
    echo ‘
    The object casting a shadow

    “;
    }
    ?>

  17. Hey All,

    I’m not sure if anyone posted a similar solution (I didn’t read all 12 pages of discussion!) but here’s one way to programatically add in the image DIVs.

    Put this in the head of your html:


    ===================================
    and then just add class=dropshadow to any IMG tag you want to have a dropshadow.

    Terry

    PS -- this code uses the lib_c.js library that was mentioned in the article "Accessible Pop-up Links" and which can be found at
    http://v2studio.com/k/code/lib/

  18. Yeah, PHP and the GDlib are go for drop-shadowing, so long as you write the image to a directory rather that have the server do the calc over and over. On-the-fly image manipulation is server-intensive. Unless you need the non-shadowed version intact yet don’t have the space for duplication.

    I did a preliminary looksee, and couldn’t get this div-div-div onion skin to shadow a table, rather than an image. Or how about another div serving the same function?

  19. First of all: nice article, William. It’s not the uber-trick i’ve expected, but as someone mentioned before, really great for photogalls and so on.

    I like and prefer Zac’s idea using JavaScript to add the extra-stuff – so the whole thing looks really clean, except the IE-Mac hack; is leaving the Maccies here without shadows an option maybe? Or should we all wait for CSS3 to become rendered in future version, maybe Opera 8 next time, or IE 8 in plenty of years? 😉

    One litte thing OT:
    Faruk Ates’ Hawk-Eyes found the background-image bug, but that could happen to everyone. So much background, -sound, -image, -color… oh, background-color? “It is all white – it is all right”.
    Sorry to complain, maybe its just my envy, but it makes me sad after reading a good article/source and statements like “…His design of Calibre.com is the company’s proud foray into standards-based design.” and then seeing there is no default bgcolor.

    Then i thought: well, let it be, it will be added soon for sure, but one minute later i discovered the same at http://www.ploughdeep.com/onionskin/ – or is this a huge shadow and not my default-lightgray background-color? 😉

    Hope you’ll fix this asap so we can del-edit my dont-post-so-late entry.

  20. Thank You:
    *Solve this problem by using tables | by smoother*

    I used the above fix to get things to display properly in Netscape 7.

    FOr some reason, the drop shadow width was expanding and breaking the layout. Placing the container in a table fixed that problem. Thanks.

  21. Hi,

    First off I’ve seen the article by Brian Williams about horizontal centering and it works a charm. The problem is that I want to center this image and it’s drop shadow not only horizontally but vertically as well. Now I’ve searched for this on the web and there is loads of info which I’ve tried but most work only with an image by itself….. as soon as I add the shadow everything goes pear-shaped. Before I clutter then forum with my code I was wondering if anybody had an elegant solution/hack? below is the layout I’m going for:

    ———————–
    |div. | div.main |
    |left | ———– |
    | | | image | |
    | | | with | |
    | | | shadow | |
    | | ———– |
    | | |
    ———————–

    i realise there is a CSS weakness with vertical centering but there must be some way of doing this as it’s such a common thing to want to do. One other thing is that the image can be of either any dimension less then the max and can be either portiat or landscape.

    Maybe I shouldn’t be using this shadow trick but it seems so much cleaner then the table version.

    Many thanks
    Simon

  22. Ok, so apparently I can’t even get posted format looking the way it should (sigh)…. let me try again (assume fullstops are whitespace!):

    ———————–
    |div..| div.main …..|
    |left.|..———–..|
    |…..|..|..image..|..|
    |…..|..|..with…|..|
    |…..|..|..shadow.|..|
    |…..|..———–..|
    |…..|……………|
    ———————–

    I was reading somewhere that you can’t actually do this with CSS so if that’s the case then that’s alright I’ll do the shadow and centering with tables. I was trying to avoid that.

    Si

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career