Web Development by Netxpect

CSS Boxes with Outside Frame (Rounded or Not)

The designer I work with wanted to have a site with information inside boxes. He also said that there would be a lot of boxes of variable height and width. More to that, the boxes needed to have a custom border all around and both the bottom side and the left one of the box would have a shadow that would be positioned above a random pattern.

That was quite a challenge!

See a live preview here…

As you all know the magic of alpha transparencies comes within PNG. Fully supported by Firefox, Safari and IE7 it really is a mess with IE6. If you don’t know how to support Alpha Gradient Transparencies in IE6 read this article here.

To start with let me show you the final result:












The designer passed me the work in a Fireworks file which came quite handy because all elements were selectable and madethe cutting very very easy.

When I was finished I had something that looked like this:

As you can see in the picture on the left all sides of the frame have been cut down to a minimum and will expand via CSS. By the time you are reading this you might have the feeling… “haven’t I read this somewhere?”… well, perhaps you have, perhaps you haven’t but what is really unique about this composition is the fact that it will expand and shrink to follow your needs keeping your bandwidth short.

‘A List Apart’ has a great tutorial on how to do boxes with CSS. The only thing I don’t like with their solution is that fact that you have to use an image with the weight and hidth as high as you intend your box to be. If it so happens that you need to max-out that pre-defined size, you have to make a new image. With my solution you only need to alter a value in a CSS file. Theirs is, however, more semantically correct than this one.

Let’s get our hands dirty.

The first thing to do is place all images inside DIVs.
<div>
<div><img src="left_top_corner.png" width="20" height="20" alt="" /></div>
<div><img src="top.png" width="100%" height="20" alt="" /></div>
<div><img src="right_top_corner.png" width="27" height="20" alt="" /></div>
<p />
<div>
<img src="left.png" width="20" height="2" class="insidePNG_left" />
<img src="right.png" width="27" height="3" class="insidePNG_right" />
<div>
Here Goes Content
</div>
</div>
<p />
<div><img src="left_bottom_corner.png" width="20" height="27" alt="" /></div>
<div><img src="bottom.png" width="100%" height="27" alt="" /></div>
<div><img src="right_bottom_corner.png" width="27" height="27" alt="" /></div>
</div>


The result can be a bit disappointing. Notice that the top.png and bottom.png files have a width of 100%! That will ensure that our box can have any kind of width. Now let’s try to position the corners correctly.

We have to define a class to the outmost DIV that will define the box’s width. The height of the box will vary according to content. We must also position the 4 corners in their places and make sure that both the top and the bottom DIV’s expand correctly.



The markup with classes.
<div class="wraper size position">
<div class="top_left"><img src="left_top_corner.png" width="20" height="20" alt="" /></div>
<div class="top size"><img src="top.png" width="100%" height="20" alt="" /></div>
<div class="top_right"><img src="right_top_corner.png" width="27" height="20" alt="" /></div>
<p />
<div>
<img src="left.png" width="20" height="2" />
<img src="right.png" width="27" height="3" />
<div class="content">
Here Goes Content
</div>
</div>
<p />
<div class="bottom_left"><img src="left_bottom_corner.png" width="20" height="27" alt="" /></div>
<div class="bottom size"><img src="bottom.png" width="100%" height="27" alt="" /></div>
<div class="bottom_right"><img src="right_bottom_corner.png" width="27" height="27" alt="" /></div>
</div>


And we then apply the style sheet to the html.
<style type="text/css">
.wraper { position: relative; padding-left: 20px; padding-right: 27px; }
.size { width: 200px; }
.top_left { position: absolute; top: -20px; left: 0px; width: 20px; height: 20px; }
.top { position: absolute; top: -20px; height: 20px; }
.top_right { position: absolute; top: -20px; right: 0px; width: 27px; height: 20px; }
.bottom_left { position: absolute; bottom: -27px; left: 0px; width: 20px; height: 27px; }
.bottom { position: absolute; bottom: -27px; height: 27px; }
.bottom_right { position: absolute; bottom: -27px; right: 0px; width: 27px; height: 27px; }
</style>


You probably have already noticed but the 20px from padding-left and the 27px from padding-right that keep repeating themselves in the CSS are the width of the left and right corners. It is imperative to declare a position to the outmost wrapper as all the corners will be positioned absolutely according to that DIV. You must also leave some space for the lef and right bars of the frame. You can accomplish this by adding some padding to that same DIV.

You’ll now have something like this.







Very nice! Much better than expected! Now we only need to adjust the left and right bars and we’re trough. Notice that I added a background color to the inmost DIV to give you a sense of space.

You can change the inmost DIV properties and the box position by using this CSS classes:
.content { background-color: #0099CC; }
.position { top: 23px; }


We’re almost there. Let us keep going! Now all we have to do is position the left and right borders and then expand the image so that it makes a continuum line. We will position the border with another added class and the image size will have to be hard-coded on the HTML. Of course you can control this through PHP, for example so that you can resize multiple boxes at once. Nevertheless you just have to keep that value high. It will not be noticeable to the user so max-it out high above all expected size. The great thing about this solution is that your box will be virtually infinite.

We add yet another class and we re-define the left and right border size.
.insidePNG_left { position: absolute; left: 0px; }
.insidePNG_right { position: absolute; right: 0px; }


<div class="wraper size position">
<div class="top_left"><img src="left_top_corner.png" width="20" height="20" alt="" /></div>
<div class="top size"><img src="top.png" width="100%" height="20" alt="" /></div>
<div class="top_right"><img src="right_top_corner.png" width="27" height="20" alt="" /></div>
<p />
<div>
<img src="left.png" width="20" height="1000px" class="insidePNG_left" />
<img src="right.png" width="27" height="1000px" class="insidePNG_right" />
<div>
Here Goes Content
</div>
</div>
<p />
<div class="bottom_left"><img src="left_bottom_corner.png" width="20" height="27" alt="" /></div>
<div class="bottom size"><img src="bottom.png" width="100%" height="27" alt="" /></div>
<div class="bottom_right"><img src="right_bottom_corner.png" width="27" height="27" alt="" /></div>
</div>


The result is ok but not yet great. As you can see the borders are now floating away from the container. All we really need is to prevent the border overflow from being seen and we can define that in CSS with overflow:hidden. We have also added a .clear class to both paragraphs in order to keep Internet Explorer happy!


So this would be the final markup.
<head>
<style type="text/css">
.wraper { position: relative; padding-left: 20px; padding-right: 27px; }
.size { width: 200px; }
.top_left { position: absolute; top: -20px; left: 0px; width: 20px; height: 20px; }
.top { position: absolute; top: -20px; height: 20px; }
.top_right { position: absolute; top: -20px; right: 0px; width: 27px; height: 20px; }
.bottom_left { position: absolute; bottom: -27px; left: 0px; width: 20px; height: 27px; }
.bottom { position: absolute; bottom: -27px; height: 27px; }
.bottom_right { position: absolute; bottom: -27px; right: 0px; width: 27px; height: 27px; }
.insidePNG_left { position: absolute; left: 0px; }
.insidePNG_right { position: absolute; right: 0px; }
.innerdiv { position: relative; overflow: hidden; margin-right: -27px; margin-left: -20px; padding-left: 20px; padding-right: 27px; }
.content { background-color: #0099CC; }
.position { top: 23px; }
.clear { font-size: 0; line-height: 0; height: 0; padding: 0; margin: 0; clear: both; }
</style>
</head>
<body>
<div class="wraper size position">
<div class="top_left"><img src="left_top_corner.png" width="20" height="20" alt="" /></div>
<div class="top size"><img src="top.png" width="100%" height="20" alt="" /></div>
<div class="top_right"><img src="right_top_corner.png" width="27" height="20" alt="" /></div>
<p class="clear" />
<div class="innerdiv size">
<img src="left.png" width="20" height="1000px" class="insidePNG_left" />
<img src="right.png" width="27" height="1000px" class="insidePNG_right" />
<div class="content">
Here Goes Content
</div>
</div>
<p class="clear" />
<div class="bottom_left"><img src="left_bottom_corner.png" width="20" height="27" alt="" /></div>
<div class="bottom size"><img src="bottom.png" width="100%" height="27" alt="" /></div>
<div class="bottom_right"><img src="right_bottom_corner.png" width="27" height="27" alt="" /></div>
</div>


VoilĂ ! There we have it. A perfect, fully expandable, box! And even though the markup may be a bit messy, I’m sure this will come in handy very often.

In the specific project I was working, in order to keep the flow clean, I just made this as a PHP function and would inject the code in the markup whenever needed.

You visit http://www.osazeitonas.com and check the box working live.

Download the fully working version here.

See a live preview here.

So you want to learn more? You can always grab a book.

If you are new to CSS and want to get into the detail of it I would recommend "CSS Web Site Design Hands on Training" by Eric Meyer.

If, on the other hand, you're looking to enlighten yourself about CSS and what can be done with it you could visit http://www.csszengarden.com/ or buy "The Zen of CSS Design" by the same author.

16 comments:

Live TV Maybe said...

really good stuff, looks great in firefox.

ElephantStew said...

Good job, one suggestion I might have for people in the future doing this is to combine the top left, top right, and top border into one image, do the same with the bottom image, so you've got an image like this

ab
cc

where a = top left, b = top right, c = top border. You can then clip the images using background position, it will get you a slightly smaller file, but more importantly less requests to the server for a new file. Depending on your implementation you can also combine the left border and the right border, but, this can be more frustrating.

Anyway, good job! =)

Francisco said...

Thank you very much for your comments. Both here and by showing some appreciation trough Digg. It motivates me to keep on writing.

Like ElephantStew said the flipping solution is much more efficient and should be used whenever possible. In the specific work I had in hands, as we had a semi-transparent frame border, it wasn't possible to implement... but css flipping does save user time and server bandwidth. Thank you for your input ElephantStew.

Anonymous said...

Someone else has also used a technique like this quite well, and has documented how he went about it:

http://home.tiscali.nl/developerscorner/liquidcorners/liquidcorners.htm

Anonymous said...

I designed a similar box for my own use once. The problem is, it adds a ton of bloat to your website just for the sake of some pretty boxes. Don't get me wrong, I know people pay to have their sited designed however they like, but anyone that end's up with a high traffic site will not be happy with slowdown.

Francisco said...

I absolutely agree with the Anonymous position. Even though this solution is aesthetically it's quite an unnecessary load to a heavy-bandwidth traffic site. If you are planning to be the next Google or Digg don't use it! For local sites that are not supposed to grow to a world-wide dimension, like a small restaurant or shop, or anything that's not going to get more than 100.000 visitors a month, do use it (if your client think it's pretty) as it really won't put any extra weight on your traffic-bill.

Thank you for your comments!

Anonymous said...

Cool but bad implementation.

CSS background properties > imgs when doing box borders.

Anonymous said...

Er, to add on to my previous reply, there is no reason that you can't achieve the same effect or even something with greater fluidity with just stuff in the CSS.

Francisco said...

Anonymous, please take into consideration that all four sides of the frame are different and so are the corners. Not only that but the box must also expand horizontal and vertical to value that is unknown to start with. In addition to that the height of the box should have the possibility to expand in order to meet the content.

Those where the objectives I had to achieve in this particular scenario. It's not pure CSS and it obviously goes against some established codding rules. I recommend the usage of this code only to people who have this particular problem and this problem only. If you just need a one pixel border frame for god sake use CSS!

Nevertheless I would be delighted if you could come up with a pure CSS solution! If you're gonna give it a go please take into consideration that it must be able to support alpha-transparent images in IE6 (in order to have a pure shadow, png image, in the right side of the frame).

Thank you for the input.

PS. It's great so many people are getting interest in this. Perhaps together we'll be able to come up with a much more efficient solution!

Jimmy James said...

You saved me plenty of time with expanding shadows. You are a CSS King. Thanks for your article look forward to reading more in future.

Regards

Jimmy

Francisco said...

Jimmy,

Thank you very much for your support.

Yours Sincerely,
Francisco

Paul said...

Dear Francisco,
thank you for this excellent instructional post - I found it while searching for a compact way of presenting liquid borders with transparency.

I've implemented it with no trouble, except for Firefox, of all things! It shows any block-level content like p or h4 tags within the class "content" with too much vertical space above and below. IE looks fine. the space disappears if the content is like yours, with no p tag.

What bug or "feature" is there in FF2 that causes this huge unaccounted for space?

Paul said...

To follow up on my post, the bug is the margin collapse issue... in this case the margin doubles between the innerdiv tag and the p or h4 tag. i made it go away by doing this:

p, h1, h2, h3, h4 { /* stops content class collapse, & equalizes effect in both IE and FF*/
margin: 0;
line-height: 150%;
}

Anonymous said...

it looks ok but

when i have a long text in content and some links from up to down like = href="#down" this overflow: hidden; will scroll inside of box :/

Francisco said...

Paul, thank you very much for your input! It works out very nicely.

As for the anchor problem. I don't have a solution for it. If anyone finds out how to work it out please let me know.

Thank you,
Francisco

Pastor Dan said...

To accomplish the same effect without putting in a large height and clipping the output, I have done the following. Make your left and right shadows/borders as background images in divs with repeat-y. Wrap both the left and right shadow/border divs around your central content. Adjust the position of the central content and right shadow with css margins. The key here is to wrap the shadow/border divs around the content so they will automatically expand to the size of the content, whatever that may be.