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.
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 div
s. Using CSS, we can stack div
s 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
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 div
s, 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 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
div
s 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 div
s 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 div
s 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 div
s 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 div
s expand with it. The result: all three shadow components, positioned along the right and bottom sides of their div
s 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
This 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.
|
|
|
8 pixels | 12 pixels | 18 pixels |
|
|
|
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 div
s 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
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.
this is fine metod! and very interesting technology! Brian Williams – thanks from all webbuilding library http://design.i2r.ru and from our gentle readership!
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.
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.
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
Astro –
It’s not as complicated as you might think. It really only takes one more div, but you’ve got to think hard about the dimensions of the shadow components you’re using. Sometimes, the pixles in each are difficult to get matched up. Pay attention to the corners – keep tweaking until you get it.
http://www.ploughdeep.com/onionskin/example2.html
Good luck.
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.
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
This technique only uses 3 divs including the one holding the content.
That is only 2 extra divs.
well, it works perfectly!
but how to center these containers?
does not work. (due to float:left???).
who can help me?
Once again…
http://www.ploughdeep.com/onionskin/centered.html
My apologies for not including this in the article.
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 =(
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.
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.
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
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?
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.
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 ‘
“;
}
?>
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.
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 ‘
“;
}
?>
These forums are not good for code. Please take out the » in the line:
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/
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?
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.
I am trying the shadows on content. I have built a sample to test here:
http://65.115.104.55/redmond_Demo/index_fluid11.html
my problem is on IE6 on pc, the top shadowed box does not show its content. Can anyone help?
It seems that every shadowed unit needs a container around it. So I have updated page from last comment: http://65.115.104.55/redmond_Demo/index_fluid11.html
also testing whether this could work for fluid column in fluid layout here:http://65.115.104.55/redmond_Demo/index_fluid12.html
This has been quite a learning adventure, and will definitely be valuable in my design treasure chest
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.
i made a onion-skinning glow version, based in this great article.
http://www.kadazuro.com/blog/archives/000067.php
tnx for the article btw.
k.
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
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