Creating a double-gradation background with CSS

Michael Meadhra recounts working on a project that needed a double-gradation background effect. He notes that this even though this didn't seem like a particularly complicated idea, implementing it was not as straightforward as you might expect.

I once worked on a project that needed a double-gradation background effect. The goal was for the white background to fade out to gray as it approached the left and right sides of the page as shown in Figure A. It didn't seem like a particularly complicated idea, but implementing it was not as straightforward as you might expect.

The double gradation effect is fairly simple to accomplish when you're working with a fixed-width layout. All it takes is a single background image of the appropriate width that incorporates the gradation on the left and right sides. Then, you create a style that applies that background image to the body tag and repeats it vertically, and you're done.

What made this project more challenging was that it was a liquid layout, which means that the background would have to expand and contract with changes in the size of the browser window. A single background image couldn't stretch to accommodate changes in the browser window size, so I needed to use separate images for the left and right gradations. And since each page element can have only one background image, I needed to resort to some trickery to build a background that included two background images, filled the viewport vertically regardless of the amount of content, and worked across all the current major browsers.

Building the basic background

Creating the double gradation background effect in a liquid layout would be easy if we could attach two background images to the body tag. One image would be attached to the left side of the body to create the left-side gradation and a second image would be attached to the opposite side of the body to create the right-side gradation. The gradations would always be attached to their respective sides of the browser window no matter how the width of the background might change. Unfortunately, it's not that simple.

Multiple background images aren't allowed on the body tag (or any other page element), but we can simulate that effect by adding a div that acts like a transparent overlay over the body element. Once we have two page elements to work with, we can attach one background image to the body tag and the other background image to the div overlay to create the double-gradation background effect. To keep the background images small, they're each just a few pixels tall by whatever width is needed to create the gradation. The images can be tiled vertically as needed to fill the page height.

The XHTML markup for this effect is quite simple. There's just one extra div (<div id="bkgnd2">) added to the markup between the <body> tag and the rest of the page content (<div id="main">).

<body>
<div id="bkgnd2">
  <div id="main">
    <h1>Double background images </h1>
    <p>That looks on tempests ...</p>
    <p>... love is not love.</p>
  </div>
</div>
</body>

The CSS code for this technique starts out fairly straightforward as well:

body {
    margin:0;
    padding:0;
    background-color:#FFFFFF;
    background-image:url(bodywrapgrad.gif);
    background-position:left top;
    background-repeat:repeat-y;
}
#bkgnd2 {
    background-color:transparent;
    background-image:url(bodywrapgrad-reverse.gif);
    background-position:right top;
    background-repeat:repeat-y
;
    text-align:center;
}
#main {
    margin:0px auto;
    width:50%;
    text-align:left;
}

The body style starts out by zeroing out the margins and padding (margin:0 and padding:0). Then it sets the background color (background-color:#FFFFFF), specifies the background image for the left-side gradation (background-image:url(bodywrapgrad.gif)), defines the starting position for the background image as the top-left corner of the page (background-position:left top), and sets it to repeat vertically (background-repeat:repeat-y).

The #bkgnd2 style defines the corresponding background settings for the right-side gradation. The background color is transparent (background-color:transparent) to allow the background from the body tag to show through. The background image file (background-image:url(bodywrapgrad-reverse.gif)) for the right-side gradation is a mirror image of the left-side gradation. The background image attaches to the right side of the div (background-position:right top) and it repeats vertically (background-repeat:repeat-y) like its counterpart from the opposite side. In this example, the main content is centered, so the #bkgnd2 style includes the text-align:center rule to center the #main div in older versions of IE.

The #main style formats the main content div. The rules in this style don't affect the double-gradation technique.

The result, shown in Figure B, is a good start, but it's not quite the effect we want. The left-side gradation is looking good, but the right side needs some work.

Working out the kinks

As usual, the challenging part of this CSS technique is working through the unexpected glitches and browser-compatibility issues. In this case, the problems are as follows:

  • The right-side gradation is only as tall as the content in the main div—it needs to extend the full height of the browser window.
  • There's a gap above the right-side gradation that shouldn't be there.
  • The height of right-side gradation isn't the same in different browsers.

After some trial and error, I came up with the following changes to the CSS code to address these problems:

body {
    margin:0;
    padding:0;
    background:#FFFFFF url(bodywrapgrad.gif) left top repeat-y;
    height:100%;
}
#bkgnd2 {
    position: absolute;
    top: 0px;
    right: 0px;
    height:auto;
    width:100%;
    min-height:100%;
    background:transparent url(bodywrapgrad-reverse.gif) right top repeat-y;
    text-align:center;
    }
* html #bkgnd2 {
    height:100%;
}
#main {
    margin:0px auto;
    width:50%;
    padding:1px;
    text-align:left;
}

First of all, I switched to the shorthand notation for the background rules. For example, the background:#FFFFFF url(bodywrapgrad.gif) left top repeat-y rule in the body style is just a more compact way to write the background-color:#FFFFFF; background-image:url(bodywrapgrad.gif); background-position:left top; background-repeat:repeat-y; rules.

The height:100% rule added to the body style is half of the solution to getting IE to expand the right-side gradation to fill the full height of the browser window. The other half of that solution is a height:100% rule for the #bkgnd2 div. However, that rule conflicts with the height setting (height:auto) for other browsers, so I added another style with an IE-specific selector (* html #bkgnd2) to feed IE its non-standard height rule.

The real secret to getting the right-side gradation to fill the height of the browser window is making the #bkgnd2 div absolutely positioned (position: absolute). Of course, the absolutely positioned div needs rules for positioning (top:0px; right:0px) and size (width:100%). The min-height:100% rule ensures that the div and its background fills the full height of the browser window even if the content doesn't, and the height:auto rule makes sure the div expands to encompass the content when it extends beyond the initial window height. IE doesn't support the min-height attribute, but the height:100% rule in the * html #bkgnd2 style has a similar effect in IE.

The gap above the right-side gradation in Figure B is caused by margin collapse—the top margin of the heading text breaks out of the containing divs and acts as a margin for the #bkgnd2 div. The standard solution for preventing margin collapse is to add padding or a border to contain the problem margin. In this case, adding 1px of padding (padding:1px) to the #main style does the trick.

The result, shown in Figure C and in Figure A, achieves the desired effect regardless of the amount of content in the #main div—and it works in all the browsers I've tested so far. (Note: This code has been tested in the current Windows versions of IE, Firefox, Netscape, Mozilla, and Opera.)