partial drop shadows

Maybe you remember my article on the design axiom css will never catch up with visual design, this week I ran into another telling example of what I stated back then. The culprit this time: drop shadows. I haven't written too many large chunks of css lately, but a single day into a new project and I'm already running into issues I assumed would've been solved by now. I found some ways around them, but pretty it ain't.

drop shadows

As a front-end developer, I never liked drop-shadows. I didn't mind those single-color x-pixel-wide fake borders we used sometimes to create a faux drop shadow effect, but whenever alpha transparency and corner roundings were involved, things got messy real quick. Sometimes you needed up to 8 extra structural wrappers (four sides, four corners) to add a full transparent drop shadow to a flexible height/width box. Those were not happy times.

With the rise of css3 we were able to put those gripes behind us. A drop shadow syntax was introduced (box-shadow) to create drop shadows at will, and all was good for a (very short) while. To make things even more perfect, all browsers supporting the box-shadow syntax also supported the rgba() color syntax, so adding transparent drop shadows was incredibly easy. Soon though, simple drop shadows just weren't enough, extra 3D effects were added and the syntax just couldn't cope anymore. To be fair to the syntax, most of those effects were ugly as hell and couldn't have been foreseen by any sane person, but once again visual design had found a way to beat css.

The problem I'm currently facing is a little different though, as in my opinion this could have been foreseen and the current syntax just isn't flexible enough (unless I'm missing something of course). What I need to do is create a drop shadow that only appears top/bottom (or left/right), but not on the adjoining sides. Currently you can only control the "source of the light" though (positioned from top left), rather than the angle from each side of the box.

To see the different solution in actions, check out the partial drop shadow test page

using two structural elements

/* html */ <div class="outer"> <div class="inner"> content goes here </div> </div> /* css */ .outer {box-shadow:0 0 10px rgba(0,0,0,0.5); (+ -vendor)} .outer .inner {margin:0 -10px; background:#fff;}

The first technique is the most robust one, but it requires two structural elements. It's actually pretty easy, just set the drop shadow on the outer element (don't forget: vendor syntax + regular syntax) and pull the inner element out of the outer element using negative margins. Set a matching background color on the inner element and you're good to go.

This only works if you have a predictable background (pattern) on your context though, as the background color set on the inner element should match the background color of your context. Not ideal, especially when you need to depend on two different structural elements. But in some cases, this is all it takes. Browser support is good too, all browsers with support for drop shadows have equally good support for negative margins.

using :before and :after

/* html */ <div class="element> content goes here </div> /* css */ .element {box-shadow:0 0 10px rgba(0,0,0,0.5); (+ -vendor); position:relative;} .element:before, .element:after {position:absolute; width:10px; top:0; bottom:0; background:#fff; content:"";} .element:before {left:-10px;} .element:after{right:-10px;}

The :before and :after hack is used for everything except making coffee these days, so of course I tried it on this particular issue. The idea is pretty much the same, the limitations too. Just create two pseudo-elements and position them over the left and right parts of the drop shadow. Color them the same color as the background and you get the impression the drop shadow only runs top and bottom.

Sadly this still doesn't solve our problem when the background on the context has a non-predicable pattern. Browser support is once again a non-issue.

using overflow:hidden;

/* html */ <div class="outer"> <div class="inner"> content goes here </div> </div> /* css */ .outer {padding:10px 0; overflow:hidden; margin:-10px 0;} .inner {box-shadow:0 0 10px rgba(0, 0, 0, 0.75); + -vendor; padding:10px 1em; background:#fff;}

The third and final method makes use of the overflow:hidden property. Once again we need two structural elements. The outer element sets a overflow:hidden and reserves top and bottom space (for the drop shadow on the inner element), the inner element receives the drop shadow.

The problem here is that the corner roundings are completely cut off, resulting in a rather ugly effect. In some cases this might suffice, but it doesn't really look finished. The upside is that we don't need to duplicate the color and we can use more complex background patterns. Once again, browser support is not something you should worry about.

conclusion

Depending on what exactly you need to accomplish there are some hacks and troubled solutions, but I still haven't found a proper way to mimic this particular effect in css. Chances are I missed something, please link me in the comments if you know of better ways.