CSS - Single Image Fluid/Fixed Rounded Bordered Corners walkthrough.

CSS properties used :

background, background-position, position margin, top, right, bottom, left, height, width, z-index, overflow

The background image

As this is a single image technique it would probably be good to have an image to use. Fortunately coming up with one is fairly easy using my favorite image editor The Gimp.

For those of you that want to skip the making of the background image, you can just download this zip'd project image files that contains the final .gif image, a Gimp .xcf file as well as an Adobe Photoshop .psd file.

Although not a walkthough complete with screen captures of how to produce the background image, the steps I followed are :

  1. Start The Gimp.
  2. Click File -> New in the main menu.
  3. Enter a Width and Height greater than the maximum width and height of the element(s) you wish to contain. (I chose 1600x3200 for width and height)
  4. Select Transparency in the Fill with: selector.
  5. Select 'Ok'
  6. On the image window that opens up, click Select -> All in the image window menu.
  7. Click Select -> Rounded Rectangle and then adjust the Radius (%) to get a suitable radius on the corners. (I chose 4%)
  8. From here, you can do a number of things. Either fill the entire selected area with a color of your choice, save the image and quit Gimp, which will result in no border effect or continue on with this Gimp walkthrough to see how I created the border effect you will see being used.
  9. Select an outside border color to show somewhat of a shadow. (I chose #C5BEBE)
  10. We are going to apply a 2px thick stroke to the border line we have currently but since Gimp will place the stroke evenly on both sides of the line, we will want to shrink the selection by 1px to have the 2px line abutt the edige. To do that, click Select -> Shrink and set the Shrink Selection By to 1px.
  11. From the Edit -> Stroke Selection window, make sure Stroke line is selected and set the Line Width: to 2.0px.
  12. Now use Select -> Shrink to shrink the selected area by half the amount of the outside shadow color plus half the primary border color. (My outside shdow color is 2px and my primary border color is 4px so shrink by 3px)
  13. Select a color for the primary border color. (I chose white, ffffff)
  14. Again using Edit -> Stroke Selection make sure Stroke line is selected and this time set the Line Width: for your desired primary border color width, in my case 2.0px.
  15. Now to create an inner shadow that will blend with the inner body background color, create a new layer in the Layers, Channels, Patterns,,, window, make sure the new layer is below the existing layre and make the new layer active.
  16. This next step is a bit of a kluge but it works. What I did was to use the paintbrush to stroke the inner shadow but since the paintbrush I used is the most opaque in the center and more transparent towards the edge, setting the stroke line so that roughly half the stroked line will be underneath the primary border color will give the desired effect for the part that sticks out from underneath the primary border color.
  17. Select the Paintbrush tool in the main Gimp menu/control area and select Circle Fuzzy (09)(9x9) in the Layers, Channels, Patterns,,, window.
  18. Use Select -> Shrink to shrink the selection area to equal the inside of the primary border color which would mean shrinking it by 2px, in the case of the image I am using.
  19. Select the same color as before, #C5BEBE, for the inner shadow border.
  20. Use Edit -> Stroke Selection to stroke the inner shadow color although this time use the Stroke with a paint tool option and select Paintbrush as the Paint Tool:.
  21. What I then did was to save the image at this point so I could use it as a base for whatever background colors I wanted for different containers.
  22. To finish up and give this a background color, in the Layers, Channels, Patterns,,, window, create a new layer and make sure it is shown below the previous two layers we have been using and make the new layer active.
  23. Since we want the container background color to start just inside of the primary border color and merge with the inner shadow color, leave the selection area the same as when the inner shadow was stroked.
  24. Select the color you would like to use for the primary background color.
  25. Use Edit -> Fill with FG color to fill the selected region and save the file.

The custom corner/border container

The custom corner/border container will be built in stages so that each step can be understood to see what CSS rules have which effect. This method will hopefully help people to not only understand how to achieve this particular effect but also, how to adapt it for other uses as well.

I will be presenting three different containers, a container with float:left, another with position:absolute and the third, no positioning. This will make it easier to see how the effect handles various positioning and size constraints. It will also hopefully show that this method of creating custom corners and/or borders is uneffected by how they are used. (ED : Uneffected except Internet Explorer 6 and to a lesser degree, version 7 although as the examples show the two problematic browsers are easily dealt with.)

To start out, let's create two containers and place some content in them. The content, to start with, will simply be text with a <br /> element to add some height. These outer containers will be what positioning and sizing will be applied to. Also, I will apply a border of a black dashed line around each one so that the various elements and how they interact can more clearly be seen.

The html for each is as follows: ( ED Note - the styling of the outer containers are not shown in the code displayed although can be viewed in the source for this page. )

<div>
  Here be content.
  <br />Here be some more content.
  <br />Here be content.
  <br />Here be some more content.
  <br />Here be content.
  <br />Here be some more content.
</div>
Stage 1
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

The left "L" element

Next we will insert the portion of the "L" that will create the left side, upper left corner and the top. During this and the following step our container will not look like much, simply a div within a div and then within a div but it is important to see each step of the construction to understand exactly how it works.

This time the left "L" portion will receive a temporary solid border of red to help identify what is what.

The html now becomes :

<div>
  <div class="ul">
    Here be content.
    <br />Here be some more content.
    <br />Here be content.
    <br />Here be some more content.
    <br />Here be content.
    <br />Here be some more content.
  </div>
</div>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

The right "L" element

Now we add the portion of the "L" that creates the right side, lower right corner and the bottom. This time, we will give the new element a temporary border of green.

<div>
  <div class="ul">
    <div class="lr">
      Here be content.
      <br />Here be some more content.
      <br />Here be content.
      <br />Here be some more content.
      <br />Here be content.
      <br />Here be some more content.
    </div>
  </div>
</div>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

Separating the hands, a two step process

Essentially what we will do in the next two steps is to first, create a space, using a margin on the left "L" element to create a space for the right "L" element to move in to. Then, in the step following this, we will use the right "L"'s top and left positioning rules to move it into the space created during the first step.

The amount of margin, and subsequent position offset that will be applied will be equal to the effective corner radius of our background image, which in the case of the image created for this project, 33px.

<style type="text/css">
.ul { 
  margin-right:33px;
  margin-bottom:33px
}
.lr {
}
</style>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

Now that we have the space for the lower right portion of the frame, it is time to move it in. (ED : Dotted blue border added around content.)

<style type="text/css">
.ul { 
  margin-right:33px;
  margin-bottom:33px;
}
.lr {
  position:relative;
  top:33px;
  left:33px;
}
</style>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

Internet Explorer 6 - Strike One!

Things look good in all reasonable CSS capable browsers but what you will see in Internet Explorer 6 is that the element we just made position:relative and then slid to the bottom and the right is now missing in the floated example from where its normal position should be. At least according to the W3C specifications regarding elements following the position:relative style rule.

Instead, Internet Explorer 6 gets confused and seems to forget where it should be and so places it in a seemingly random location on the page.

It is interesting to note though that this only occurrs for the float:left instance. Why not for the others? Asking that would be assuming there is some rhyme or reason, so we won't ask.

But, Internet Explorer 6 is easily corrected by simply adding position:relative to the outer-most container which will then give Internet Explorer 6 the reference it seems to need.

(ED : all following examples will include the correction for Internet Explorer 6. At the end of this article, all Internet Explorer 6 specific CSS rules will be separated out so that their use in a style sheet included using Conditional Comments can more easily be performed.

Filling in the missing pieces

As you can see the upper left "L" element has its upper left corner all to itself and the lower right "L" element has its corner all to itself so we don't have to worry about any bothersome show-through of any background elements, other than whatever is behind the container which we definitely want to show through the transparent regions outside the custom corners.

But, there are a couple of things missing. You notice the upper right corner and lower left corner just begging to be attended to?

What would seem to fit best in those places? A couple of position:absolute corners maybe?

Both corners are free of either of the "L" elements' current backgrounds so we can easily place corners in each of them and not have to worry about anything showing through that shouldn't.

In this case though, instead of using borders to highlight the different elements, I'll simply use the background image we intended to use all along.

Note : although the position:absolute corners can be located just about anywhere within the main outer container, I will place them just inside the left Hand Frame element so that their positions will be relative to the proper element.

Accordingly, since the outer left "L" element's div will be the container for the position:absolute upper right and lower left corners, the left hand frame element will now need to be position:relative so as to give the position:absolute elements their proper reference.

Also, since the corner elements will essentially be empty we can use span elements. Div elements could just as easily be used but since we don't need to use divs, we won't.

The HTML/XHTML will now look like :

<div>
  <div class="ul">
    <span class="ur"></span>
    <span class="ll"></span>
    <div class="lr">
      Here be content.
      <br />Here be some more content.
      <br />Here be content.
      <br />Here be some more content.
      <br />Here be content.
      <br />Here be some more content.
    </div>
  </div>
</div>

And, the CSS looks like :

<style type="text/css">
.ul { 
  position:relative;
  margin-right:33px;
  margin-bottom:33px
}
.lr {
  position:relative;
  top:33px;
  left:33px
}
.ur {
  position:absolute;
  top:0;
  right:0;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat top right
}
.ll {
  position:absolute;
  bottom:0;
  left:0;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat bottom left
}
</style>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

Internet Explorer 6 - Strike 2

If you are looking at this in Internet Explorer 6, you must be thinking that things are looking not as bad as one might expect, for Internet Explorer 6.

The float:left and position:absolute versions actually look good, but the problem is, neither of them should look even close to being good.

According to the W3C specifications when top, left, right or bottom are used to position an element, the distance given should be measured between like parameters. For example when left is specified, the distance is from the left edge of the element to the left edge of the containing element or with respect to the left edge of where the element should normally be rendered.

When right is specified, it should be the distance from the right edge of the element to the right edge. Similarly bottom should be between the bottom of the element to the bottom edge.

Internet Explorer 6 though has other ideas. Basically both left and right measure from the left edge of the positioned element and top and bottom are both measured from the top.

So even though it may look OK in Internet Explorer 6, it shouldn't. But, the future is at least a bit brighter as Internet Explorer 7 does get it right!

Internet Explorer 6 - Strike 3

Another problem with Internet Explorer 6 is noticeable in the lower position:static container. Internet Explorer 6 seems to have forgotten about the lower left corner element.

Fortunately the missing corner can be fixed by adding display:inline-block to an outer containing div and since we use a container div to position the rounded corner container, we can add the Internet Explorer 6 only rule to that on an as needed basis.

(ED : Following rendering examples will include the Internet Explorer 6 fix added in.)

Sliding the corners home

The next step is to slide the position:absolute corners to where they should be.

<style type="text/css">
.ul { 
  position:relative;
  margin-right:33px;
  margin-bottom:33px
}
.lr {
  position:relative;
  top:33px;
  left:33px
}
.ur {
  position:absolute;
  top:0;
  right:-33px;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat top right
}
.ll {
  position:absolute;
  bottom:-33px;
  left:0;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat bottom left
}
</style>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.

Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

Sliding the content into place

One thing remaining before adding the left, upper left, top and right, lower right and bottom borders is to position the contained content so that it is centered in the styled container. Fortunately it is as easy as it was to slide the lower right "L" element into place.

We first insert the content into a div and then insert that div into the class="lr" div. Then applying position:relative to the new div containing the content and offsetting it to the top and the left as close to half the corner radius as possible, our content will then become centered.

It would have been better had our corner radius worked out to an even number but the problem originated way back during the creation of the image due to only being able to set the radius of the corners of the selection box to a whole percentage. But, with instructions as to how to go about creating your own backgrounds of any dimensions, coming up with an even valued radius should not be that hard. So in this case, we will use an offset of -16px and it will look fairly decent.

So the new HTML looks like :

<div>
  <div class="ul">
    <div class="ur"></div>
    <div class="ll"></div>
    <div class="lr">
      <div class="content">
        Here be content.
        <br />Here be some more content.
        <br />Here be content.
        <br />Here be some more content.
        <br />Here be content.
        <br />Here be some more content.
      </div>
    </div>
  </div>
</div>

And, the CSS looks like :

<style type="text/css">
.ul { 
  position:relative;
  margin-right:33px;
  margin-bottom:33px
}
.lr {
  position:relative;
  top:33px;
  left:33px
}
.lr div {
  position:relative;
  top:-16px;
  left:-16px;
}
.ur {
  position:absolute;
  top:0;
  right:-33px;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat top right
}
.ll {
  position:absolute;
  bottom:-33px;
  left:0;
  height:33px;
  width:33px;
  background:url(rounded-border.gif) no-repeat bottom left
}
.content {
  position:relative;
  top:-16px;
  left:-16px
}
</style>
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be float:left content.
Here be some more float:left content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.
Here be position:absolute content.
Here be some more position:absolute content.

Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.
Here be position:static content.
Here be some more position:static content.

To see the completed containers including the full edges and background, check out Sliding L's without Javascript and Sliding L's with Javascript