The Seven C's Border Technique

Continuously Changing Curved Clear Corners Containing Content

Screenshot of client designA client presented me with an existing visual design for their new web site. They had the layered image in Photoshop from their graphic artists and wanted me to convert it to working HTML/CSS. Among other elements, the design featured boxed regions with transparent large-radius curved corners on a gradient background.

Requirements

The relevant design requirements are as follows:

Continuously Changing
The boxes are fluid both horizontally and vertically.
Curved Corners
The boxes have curved corners.
Clear
The area outside the curved corners but within the enclosing rectangular box is transparent (because the page's underlying background image is not a solid color and needs to shine through).
Containing Content
Because the radius of curvature of the corners is so large, the box's content appears within the bounding box of corner image.

Continuously Changing Curved Clear Corners...

The "Continuously Changing Curved Corners" requirements are easy; one simple approach uses four nested boxes, each with a background image for one of the corners, with the actual content inside the content (innermost) box.

The "Clear" requirement is trickier; using the simple approach, the solid background color of the content box fills its entire rectangle including the space outside the curved corners. A little research led me up Roger Johansson's excellent article Transparent custom corners and borders which shows one way to make curved corners transparent. His example looks like this:

Est etiam processus dynamicus qui sequitur mutationem! Qui sequitur mutationem consuetudium ...

He also shows how to use JavaScript to avoid putting non-semantic divs into the HTML source. See below.

... but not Containing Content

Unfortunately, Johansson's solution doesn't meet my "Containing Content" requirement because it assumes the corners have a relatively small radius. Here's how it looks with 100px-radius corners:

Est etiam processus dynamicus qui sequitur mutationem! Qui sequitur mutationem consuetudium lectorum Mirum est notare quam! Mirum est notare quam littera gothica quam nunc putamus parum claram anteposuerit, tempor cum soluta nobis eleifend option congue nihil imperdiet doming, decima Eodem modo typi qui nunc nobis videntur parum clari fiant sollemnes. Nihil imperdiet doming id quod mazim placerat facer possim assum Typi non habent.

I tried various ways of using relative positioning and negative margins to move the content box into the corner areas but all of them ended up causing one part or another of the box border to show up somewhere it shouldn't.

Corners Containing Content

The crux of the issue is that the content of the box must overlap the corner images. My solution is to use absolute and relative positioning (a.p. and r.p.) to put everything into place. I start with a top-level container called a "pod" which is r.p. so as to make it a containing block for all a.p. child elements. I then create six (!) non-semantic a.p. child boxes (upper left, upper right, left, right, lower left, and lower right) to display the various parts of the box border plus a seventh child box to hold the actual content.

The HTML source looks like this:

<div class="pod"> <div class="pod-ul"></div> <div class="pod-ur"></div> <div class="pod-left"></div> <div class="pod-right"></div> <div class="pod-ll"></div> <div class="pod-lr"></div> <div class="pod-content"> Est etiam processus ... </div> </div>

As with Johansson's approach, the Seven C's technique uses two images: pod-box.gif, containing all four corners of the border (which do not need to be symmetric), and pod-borders.gif, containing just the left and right vertical borders of the box. Both images need to be as wide as the widest box you will ever render with them; mine are currently 1600px wide.

The CSS is straight forward, with all six of the markup divs being basically the same. For example:

.pod { /* be a containing block */ position: relative; } .pod-ul { position: absolute; width: 51%; height: 100px; top: 0px; left: 0px; background: url(pod-box.gif) top left no-repeat; }

.pod is r.p. so it is a containing block for its a.p. children. .pod-ul is then a.p. in the upper-left corner (top: 0px, left: 0px) of .pod. Its background image is the top left corner of pod-box.gif, anchored in the top left corner of the div. The height is set explicitly to the height of the curve in pod-box.gif (100px) and the width is set to 51% so it will always meet its right-side counterpart. The corner images are not repeated, but .pod-left and .pod-right repeat pod-borders.gif along the Y axis so the boxes can be as tall as necessary. See the sample code linked at the end of the article for full details.

The end result looks like this:

This is floated to the right.

Est etiam processus dynamicus qui sequitur mutationem! Qui sequitur mutationem consuetudium lectorum Mirum est notare quam! Mirum est notare quam littera gothica quam nunc putamus parum claram anteposuerit, tempor cum soluta nobis eleifend option congue nihil imperdiet doming, decima Eodem modo typi qui nunc nobis videntur parum clari fiant sollemnes. Nihil imperdiet doming id quod mazim placerat facer possim assum Typi non habent.

Notice how the content is nestled up within the bounding rectangle of the curved corners. Success!

IE Fixes

Surprise! The standards-compliant CSS does not work in IE/Win 6. I'm not an IE-bugs expert but here is what I've figured out:

  1. When an a.p. element has both a top and bottom property value, IE does not correctly set its height relative to its containing block. The only solution I've found is to set the height explicitly on load and resize with JavaScript. I use a small bit of jQuery: function pod_fix() { $(".pod-left,.pod-right").each(function() { $(this).height($(this).parent().height() - $(this).siblings(".pod-ul").height() - $(this).siblings(".pod-ur").height()); };); }; $(document).ready(pod_fix); $(window).resize(pod_fix); This sets the height of the .pod-left and .pod-right divs to the height of the entire pod less the height of the curved corners. Note that in the example source code (see below), the jQuery code is written to scan the DOM only once instead of on every resize so it is more efficient but slightly more complex.
  2. In Quirks mode, IE requires hasLayout on the top-level r.p. box. I use the height:1px trick.
  3. In Quirks or Strict mode, floated children of r.p. parents disappear. I add zoom:1 to the innermost content box.

Cleaning the Markup

If you do not like putting non-semantic markup into your HTML, you can have JavaScript do it for you. This has nothing specific to do with pods; it is just DOM manipulation. Again, I use a bit of jQuery: function pod_markup() { $(".pod-plain") .wrap("<div class='pod'></div>") .removeClass("pod-plain").addClass("pod-content") .before('<div class="pod-ul"></div><div class="pod-ur"></div>' + '<div class="pod-left"></div><div class="pod-right"></div>' + '<div class="pod-ll"></div><div class="pod-lr"></div>'); } The code works like this:

  1. Find all elements of class pod-plain.
  2. Wrap the pod-plain element in a div of class pod.
  3. Change the class pod-plain into pod-content.
  4. Insert the six non-semantic divs before pod-content.

Actually, the code I use for this in the example is slightly more complex because it preserves additional class settings on the pod-plain div. The upshot is that when you write <div class="pod-plain some-class">... content goes here...</div> you get a fully-styled pod whose top-level div also has the class some-class. Finally, you can style the pod-plain class in case JavaScript is not enabled.

Where it works

I have tested the Seven C's Border Technique in Firefox 1.0 and 1.5, IE/Win 6, and Safari.

As shown in the example code, pods can be in the normal flow, floated, or aboslutely positioned (and presumably relatively positioned). In Firefox, at least, they can also be fixed positioned. If you try to assign position: fixed to any pod in IE, however, all pods on the page stop working. I have not looked for a workaround.

I'm interested in hearing about other circumstances under which pods work or don't work.

Drawbacks

The obvious drawback of this approach is that it requires six non-semantic divs for each pod, either in the markup or added by JavaScript. Whatever.

Another drawback is that, since the upper and lower pod corners are not in the same flow, if your pod gets shorter than the sum of their heights they will overlap and produce ugly results. If you make this page more than about 1200px wide, you'll see it. Presumably if you are using pods with large-radius corners it is because you know they will contain a fair bit of content; if not, beware.

Finally, there is a slight gotcha with using alpha-transparent PNGs (besides the fact that IE does not support them). In order to make the left and right sides of pod borders resize correctly on a variety of browsers, they each have "width: 51%". Using "width: 50%" leaves a gap under some circumstances, but "width: 51%" causes the left and right sides to overlap slightly in the middle. If your border uses partial alpha transparency, the parts that overlap will be twice as opaque (actually, the opacity will be squared) and thus somewhat darker than the non-overlapping parts. In practice I have found this not to be really noticeable unless you look for it, but it is there.

Sample code

Here is sample code for the Seven C's Borders Technique.

If you have any suggestions or improvements, let me know.