Progressive enhancement is a good thing, and CSS 3 is even better. Combined, they enable designers to create lighter, cleaner websites faster and easier than ever before..

CSS 3 can do some pretty amazing stuff: text shadows, rgba transparency, multiple background images, embedded fonts, and tons more. It’s awesome, but not all browsers are up to snuff. As designers, it’s up to us to decide which browsers to support for our projects. While everyone has their own particular strategy, there seem to be three general approaches:

Support all browsers with perfect fidelity – not realistic for most budgets, requires many elaborate workarounds, hacks, etc., also difficult to maintain, upgrade, and extend.

– not realistic for most budgets, requires many elaborate workarounds, hacks, etc., also difficult to maintain, upgrade, and extend. Support all browsers to some degree – focus first on the latest and greatest browsers, and then go back and make sure that older browsers look and work reasonably well.

– focus first on the latest and greatest browsers, and then go back and make sure that older browsers look and work reasonably well. Support newer browsers, forget about the older stuff – make your sites look pixel-perfect on the newest versions of modern browsers and don’t worry about anything else.

Among these generalized strategies, the second approach comes closest to the concept of progressive enhancement. In practice, progressive enhancement enables designers to design websites according to some predetermined support baseline and then gradually improve and optimize appearance and functionality to accommodate the most advanced browsers. Closely related to this idea is the principle of graceful degradation, which is what should happen when newer design methods aren’t understood or supported by certain browsers. Consider the following example..

A Quick Example

Consider a basic layout done with good ‘ol CSS 2.1 and HTML 4.01. We would begin with a clean layout that looks and works good in just about any browser. We’re talking basic styles here, stuff like:

basic layout and composition

background, border, and font colors

font families, styles, and transformations

basic styles for HTML elements

elements decorative graphics, link styles and so on

The idea here is to begin with a nice, well-styled presentation that looks good in even archaic browsers like IE 6. This is a good thing because even visitors using crappy browsers will be able to read and interact with your content. But instead of stopping there, progressive enhancement says, “let’s provide some additional features for people using better browsers.” After all, people using awesome browsers like Firefox, Safari, and Opera want the best experience possible from the Web. Progressive enhancement says, “let’s give it to them, but only after the less-capable crowd has been taken care of first.”

CSS 3 and Progressive Enhancement

Progressive enhancement provides a well-defined strategy for implementing CSS 3 into the presentational layer of our designs. Rather than worrying about universal pixel-perfection and trying to accommodate unsupportive browsers with all sorts of filters, hacks, and scripts, we can take advantage of the CSS cascade and deliver styles according to each browser’s capabilities. If IE doesn’t get the whole “rounded corners” thing, then it’s gonna get some square corners instead. Is this a big deal? Only if it interferes with the user’s ability to read and use the content. Is it fair? Heck no, but there is nothing stopping people from upgrading to a better browser (they’re free after all). Meanwhile Safari 4 is gonna rock those rounded corners just fine.

Progressive enhancement enables us to establish a solid baseline of cross-browser support and then enhance the design with advanced CSS features for supportive browsers.

The key thing of course is to begin with a solid baseline design that works well in as many browsers as possible. Once this is done, there is no reason to worry about which browsers support which CSS 3 properties because your site is going to work fine regardless of support. Then, in those situations where some sort of advanced support is desired, you can always go in and fix things up as needed. There are a wide variety of solutions for supporting the latest and greatest CSS 3 tricks, including browser detection, proprietary expressions, conditional comments, CSS hacks and other workarounds. Indeed, you can go as crazy as you want to, but the whole point of this article is to explain that you can avoid the fuss by progressively enhancing your site instead.

Do More with Less Code

Why even bother with CSS 3? Because you can do more with less code. Take rounded corners, for example. The old way of styling a box with rounded corners involved splicing up images and adding multiple, non-semantic elements to the markup. This method still works, but it is more time-consuming and resource-intensive than simply adding a slice of CSS to your stylesheet:

.rounded-corners { -webkit-border-radius: 7px; -khtml-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; }

The benefits of using the CSS technique over the image-based method include:

Requires less bandwidth usage

Reduces number of HTTP requests

requests Requires fewer server resources

Less time required to implement

Safeguards against browser updates

Creates a more flexible site design

Sound good? Now let’s get into the practical side of things and learn how to use progressive enhancement to implement some awesome CSS -3 techniques.

Table of Contents

Multiple Backgrounds ↩

Multiple background images via CSS enables us to do some awesome multi-layer graphics without all the extraneous markup to make it work. Using CSS 3’s multiple-background functionality is an excellent way to enhance your design while simplifying your markup. Here is the general technique:

.multiple-bg-images { background: url(http://domain.tld/path/layer-01.png) no-repeat 0 0; background: url(http://domain.tld/path/layer-01.png) no-repeat 0 0, url(http://domain.tld/path/layer-02.png) no-repeat 0 0, url(http://domain.tld/path/layer-03.png) no-repeat 0 0, url(http://domain.tld/path/layer-04.png) no-repeat 0 0; }

Alternately, we can use the following syntax:

.multiple-bg-images { background: url(http://domain.tld/path/layer-01.png) no-repeat 0 0; background-image: url(http://domain.tld/path/layer-01.png), url(http://domain.tld/path/layer-02.png), url(http://domain.tld/path/layer-03.png), url(http://domain.tld/path/layer-04.png); background-repeat: no-repeat, no-repeat, no-repeat, no-repeat; background-position: 0 0, 0 0, 0 0, 0 0; }

This works just like single background images in CSS 2.1, but now we can add up to four background images using additional, comma-separated property values, each of which can have its own specific parameters such as repeat and position . The images will be displayed such that each subsequent image appears beneath the previous one. Unsupportive browsers will ignore the second background property and render the first rule, which should display our baseline, fallback image.

Background Sizing ↩

Although current support for CSS 3’s background-size property exists only through vendor-specific properties, the following technique enables us to size backgrounds in a large percentage of browsers:

.background-size { background-image: url(http://domain.tld/path/bg.png); -webkit-background-size: 50% 50%; /* Safari */ -khtml-background-size: 50% 50%; /* Konquer */ -moz-background-size: 50% 50%; /* Firefox */ -o-background-size: 50% 50%; /* Opera */ background-size: 50% 50%; /* CSS3 */ }

The background-size property enables us to scale the background image according to our design needs. This is useful for maintaining specific proportions when using background images for <div> s, heading elements, and of course <body> and <html> elements as well.

Border Images ↩

CSS 3’s border-image property opens the door to many new graphical layout possibilities. The border-image property is complicated, but provides a powerful tool once it is understood. Browser support for border-image is growing, and there is good support for vendor-specific prefix-properties. Here is an example implementation that works in many modern browsers:

.border-image { border: solid transparent; border-width: 10px 20px 10px 20px; -webkit-border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; -khtml-border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; -icab-border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; -moz-border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; -o-border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; border-image: url("http://domain.tld/path/border.png") 10 20 10 20 round round; }

First we are specifying our default border. This defines the area in which the border image will be displayed, and also serves as a fallback for unsupportive browsers. The next six declarations specify the border-image property for different browsers. Each of the property values indicates the image location, the slice area, and the repeat style. There are also several additional parameters that further extend the functionality of the border-image property. Here is how we would target each of the four side-images and each of the four corner-images separately:

.border-image { border-top-image: url("http://domain.tld/path/border-top.png") 10 20 10 20 round round; border-right-image: url("http://domain.tld/path/border-right.png") 10 20 10 20 round round; border-bottom-image: url("http://domain.tld/path/border-bottom.png") 10 20 10 20 round round; border-left-image: url("http://domain.tld/path/border-left.png") 10 20 10 20 round round; border-top-left-image: url("http://domain.tld/path/border-topleft.png") 10 20 10 20 round round; border-top-right-image: url("http://domain.tld/path/border-topright.png") 10 20 10 20 round round; border-bottom-left-image: url("http://domain.tld/path/border-bottom-left.png") 10 20 10 20 round round; border-bottom-right-image: url("http://domain.tld/path/border-bottom-right.png") 10 20 10 20 round round; }

As you can see, the new border-image property is powerful and flexible, making it possible to design any style of border quickly and efficiently. To get an idea of the possibilities, check out the new CSS Backgrounds and Borders Module at the W3C website.

Rounded Corners ↩

Rounded corners can enhance the appearance of many designs. For years, designers have been using multiple background images and superfluous markup and/or JavaScript to get the job done. Using CSS 3’ border-radius property, rounding corners to any radius is a breeze:

.rounded-corners { border: 1px solid #171717; -webkit-border-radius: 7px; -khtml-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; }

We can also round specific corners like so:

.rounded-corner-top-left { -webkit-border-top-left-radius: 7px; -khtml-border-radius-topleft: 7px; -moz-border-radius-topleft: 7px; border-top-left-radius: 7px; }

Combined with multiple background images, border images, and drop shadows, rounded corners makes designing beautiful sites easier than ever before. As discussed previously, browsers that do not support rounded corners will have to suffer with – gasp! – square corners. This is a perfect example of how to progressively enhance your designs with CSS 3. Also check out these easy copy-&-paste rounded-corner recipes.

Drop Shadows ↩

Drop shadows are another great example of a design element that should be applied via progressive enhancement. As cool as they are, there is no reason to splice up a bunch of images, implement a PNG fix, and add extra markup and styles just to ensure that crusty old versions of IE are gonna get some drop-shadow embellishments. It’s like, who cares? With CSS 3, we can deliver customized drop shadows to all good browsers with a few lines of code:

.single-drop-shadow { border: 1px solid #333; box-shadow: 1px 1px 7px #999; -moz-box-shadow: 1px 1px 7px #999; -webkit-box-shadow: 1px 1px 7px #999; }

The box-shadow property accepts four parameter values, x-axis , y-axis , blur , and color (in that order). These parameters control the x-y offset, shadow-blur, and shadow-color, respectively. Note that drop shadows will only be rendered on elements that have a border applied. If we’re feeling frisky, we can also throw down multiple drop-shadows on a single element:

.single-drop-shadow { border: 1px solid #333; box-shadow: 1px 1px 7px #999, 1px 1px 10px #cc0000; -moz-box-shadow: 1px 1px 7px #999, 1px 1px 10px #cc0000; -webkit-box-shadow: 1px 1px 7px #999, 1px 1px 10px #cc0000; }

Here we are combining two drop shadows with similar offsets, but with different blurs and colors. And you can even get something similar on IE using some proprietary Shadow filters:

.ie-shadow { -ms-filter: /* IE8+ */ "progid:DXImageTransform.Microsoft.Shadow(color=#999999, direction=0, strength=7) progid:DXImageTransform.Microsoft.Shadow(color=#777777, direction=90, strength=10) progid:DXImageTransform.Microsoft.Shadow(color=#777777, direction=180, strength=10) progid:DXImageTransform.Microsoft.Shadow(color=#999999, direction=270, strength=7)"; filter: /* IE<8 */ progid:DXImageTransform.Microsoft.Shadow(color=#999999, direction=0, strength=7) progid:DXImageTransform.Microsoft.Shadow(color=#777777, direction=90, strength=10) progid:DXImageTransform.Microsoft.Shadow(color=#777777, direction=180, strength=10) progid:DXImageTransform.Microsoft.Shadow(color=#999999, direction=270, strength=7); }

Likewise, we could use the dropshadow filter to achieve a similar effect:

.ie-drop-shadow { -ms-filter: /* IE8+ */ "filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=7, OffY=7, Color=#555, Positive=true)"; filter: /* IE<8 */ filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=7, OffY=7, Color=#555, Positive=true); }

This kind of proprietary nonsense makes the concept of progressive enhancement extremely attractive.

Text Shadows ↩

Another subtle visual enhancement is possible with some strategically placed text shadows. Certainly no need to create image replacements for your shadowed text, just throw down a few lines of CSS 3 for supportive browsers and deliver regular text styles to less-capable browsers:

.text-shadow { text-shadow: 1px 1px 3px #777; }

Nothing to it. The text-shadow ’s parameters are, in order, x-axis offset, y-axis offset, shadow blur, and shadow color. Multiple shadows are also possible:

.text-shadow { text-shadow: 1px 1px 3px #777, 2px 2px 7px #999; }

Simply separate multiple shadows with commas and you’re good to go. If you want to support IE , add the following filter declarations to your .text-shadow class:

.ie-text-shadow { -ms-filter: /* IE8+ */ "progid:DXImageTransform.Microsoft.MotionBlur(strength=1, direction=220) progid:DXImageTransform.Microsoft.Blur(pixelradius=3) dropshadow(color=#777777, offx=1, offy=1)"; filter: /* IE<8 */ progid:DXImageTransform.Microsoft.MotionBlur(strength=1, direction=220) progid:DXImageTransform.Microsoft.Blur(pixelradius=3) dropshadow(color=#777777, offx=1, offy=1); }

Again, this sort of monstrosity is easily avoided by delivering the text shadows to capable browsers and not worrying about the dinosaurs. Seriously, do you think anyone using Internet Explorer is going to notice the difference? Chances are good that they couldn’t care less.

Alternate Row Styles ↩

Before CSS 3, alternating styles for odd and even rows of content required server-side shenanigans or something something JavaScript. Now, CSS 3 makes it easy to style odd, even, or any nth number of rows using a few slices of code.

li:nth-child(even) { background: green; } li:nth-child(odd) { background: yellow; } li:nth-child(3n) { background: white; }

As a matter of fact, CSS 3 makes it easy to target just about any specific element:

li:nth-of-type(3) { /* selects the third list element */ } ul:nth-child(7) { /* selects the seventh list element */ } li:first-of-type { /* selects the first list element */ } li:last-of-type { /* selects the last list element */ }

99% of the time, alternating and custom element styles are going to be an added usability and/or aesthetic bonus and not required for ancient browsers. If the situation does call for custom row styles in all browsers, just do what everyone has been doing for years: add a class to the element and style accordingly.

Opacity & Transparency ↩

This method is widely known, and generally applied in mindless “copy-&-paste” fashion. The following rules work great together, targeting virtually every browser in use today. Here is how to specify specific degrees of opacity to any element:

.opacity { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(opacity=70)"; filter: alpha(opacity=70); /* internet explorer */ -khtml-opacity: 0.7; /* khtml, old safari */ -moz-opacity: 0.7; /* mozilla, netscape */ opacity: 0.7; /* fx, safari, opera */ }

In case the opacity property is new to you, this code will set the opacity of the target element to 70 percent. A value of “ 1.0 ” specifies complete opacity, while a value of “ 0.0 ” will render the element invisible. For more information, check out Cross-Browser Transparency via CSS.

Custom Fonts ↩

Web designers have pretty much been stuck with an extremely limited set of “web-safe” fonts, which includes Verdana, Georgia, and the ubiquitous Arial font. There are numerous JavaScript/Flash-based workarounds that enable custom fonts, but the process is laborious, tedious, and full of caveats. Thankfully, CSS 3 is taking us into the future with its piping hot @font-face property. As you have probably heard, @font-face enables you to easily embed any custom font using pure CSS :

@font-face { font-family: "Museo"; src: url("fonts/Museo.otf") format("opentype"); } h1 { font-family: Museo, Georgia, serif; }

As with many of CSS 3’s new properties, @font-face is going to benefit supportive browsers, which currently includes Firefox 3.1+, Safari 4.0+, and Opera 10+. There are also ways of making it work in Internet Explorer, but I’m going to save that for a future article.

In this example, we specify the font’s name and location in the @font-face selector, and then specify its name in the h1 selector. It’s that easy. And best of all, cool cats using latest versions of Firefox, Safari, Opera, and Chrome are going to enjoy your custom fonts as fluently as if they were installed on their own machine. Unsupportive browsers will simply fall back to either “Georgia” or the system default serif font.

Custom Text-Highlight Styles ↩

By default, most browsers display selected text with a boring blue color. Changing this to a custom color is a good way to improve the overall harmony and thematic consistency of your design. Here at Perishable Press, I enhance the aesthetic functionality of the Serious Theme using the following slice of CSS 3:

*::selection { background: #E6E5C3; color: #291F16; } *::-moz-selection { background: #E6E5C3; color: #291F16; }

If you happen to be visiting via the Serious Theme, you can check this out by highlighting some text. Even works great with <pre> code!

Multi-Column Content Display ↩

Presenting your blog content in single columns is boring, but is certainly acceptable for older browsers. For newer, supportive browsers, you can use CSS 3’s multi-column layout properties to format your blocks of text in sleek, magazine-style multi-column format. To improve support for this feature, we throw some browser-specific prefixes into the mix:

.multiple-columns { -moz-column-count: 3; -moz-column-gap: 33px; -webkit-column-count: 3; -webkit-column-gap: 33px; column-count: 3; column-gap: 33px; }

As is, this sample code will render the content of our target <div> with three columns spaced 33 pixels apart.

Box Sizing ↩

This is a new one to me, as I have never actually used it before. The concept is straightforward though, so you can relax. With the conventional level-2.1 box model, applying padding and borders to block-level elements can be unpredictable and frustrating. Now, with CSS 3’s box-sizing property, designers have full control over how padding and borders are added to elements. Here’s the juice:

.box-sizing { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; }

Here we are using the border-box value to ensure that padding and borders are subtracted from the existing width/height of the element, such that the specified width/height remain the same in either case. Alternately, we could stick with the classic box model and add the padding/borders to the existing width/height, such that the total width increases according to the total size of the added padding and borders. You definitely want to experiment a little with this method to fully appreciate its potential. Also, of all the techniques in this article, this is probably the least recommended candidate for progressive enhancement because of potential layout discrepancies in non-compliant browsers.

RGBa Colors and Transparency ↩

Lastly, one of the coolest things about CSS 3 is that it brings alpha-transparency to the RGB color palette. This new property is called rgba and is a powerful way to specify the level of opacity or transparency of an element. Here are a couple of examples:

.transparent-background-color { color: rgb(0, 0, 0); background-color: rgb(77%, 77%, 77%); background-color: rgba(255, 255, 255, 0.7); } .transparent-text-color { color: rgb(77%, 77%, 77%); color: rgba(0, 0, 0, 0.7); background-color: rgb(255, 255, 255); }

This method is superior to the previously discussed opacity property because with rgba we can control the opacity of different properties of specific child elements. With opacity , it’s all or nothing. Within either of the rgb or rgba properties, the order of values is red, green, and blue, which are specified as either 0-255 or 0%-100% . Then, for the rgba property, the fourth value specifies the opacity level, which ranges from “ 0.0 ” (transparent) to “ 1.0 ” (opaque).

Notice that we include a fallback color for unsupportive browsers. This is accomplished by preceding each rgba property with an rgb equivalent. Browsers that don’t “get” the alpha-transparency stuff will fall back to the first declaration. Also note that there is a filter to get alpha transparency working on IE :

.ie-transparent-background-color { background: transparent; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#FF333333, endColorstr=#FF333333); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#FF333333, endColorstr=#FF333333)"; zoom: 1; }

..and, you’re probably gonna want to include that nasty thing via conditional comments.

Word Wrap ↩

If you display a lot of preformatted ( <pre> ) text, you know how difficult it can be when dealing with continuous character strings. In particular, long URL s have a nasty habit of breaking layouts, not only from preformatted text, but paragraphs, headings, and just about any other element for that matter. Thankfully, the W3C has adopted Microsoft’s extremely useful word-wrap property and included it in the CSS 3 specification.

.break-word { word-wrap: break-word; }

When applied to an element, this declaration will break long strings of text and wrap them onto the next line. The word-wrap property accepts one of two values: “ normal ” and “ break-word ”. Simple and effective.

Resizable Elements ↩

This is a sweet little feature that enables the user to grab the corner of any element — not just <textarea> s — and resize it according to their needs. Not sure about which browsers support this at the moment, but it’s just a matter of time before most of them understand something like this:

.resize { resize: both; }

I think this one of the coolest and most practical new features of CSS 3, and one that designers should begin implementing immediately, especially for smaller-sized <textarea> s.

One particular CSS 3 feature that is exciting to designers is the transition property, which essentially animates the transition between two display states of an element. Hyperlink hover states make a good example. Consider the following styles applied to your anchor elements:

a:link, a:visited { background: red; color: white; } a:hover, a:active { background: white; color: red; }

With these styles, your links are going to have a red background with white text. When the user hovers over these links, the background is going to change to white and the text is going to change to red. As-is, the transition between these two states happens virtually instantaneously, but with the transition property, we can actually animate the transition between the background and color values:

a:link, a:visited { -webkit-transition: all 1s ease; transition: all 1s ease; background: red; color: white; } a:hover, a:active { background: white; color: red; }

With that code in place, supportive browsers will show a one-second “ ease ” transition between “ all ” properties within the selector ( background and color in this example). The transition can also be modified to affect specific properties and specific transition durations. There are also several different types of transitions styles, including ease , linear , ease-in , ease-out , and ease-in-out . Endless possibilities here that are completely suitable for immediate progressive enhancement of your designs.

CSS Color Gradients ↩

On the very cutting edge of browser support for CSS 3 is the ability to create color gradients using pure CSS . Strategically applied, gradients enhance the visual appearance of just about any design, especially when combined with other graphical elements, such as background images, fancy borders, and well-styled text. Creating gradients with CSS eliminates yet another HTTP request from the performance equation. Plus they scale better too.

.color-gradients { background-image: -moz-linear-gradient(top, #FFFF00, #FF0000); background-image: -webkit-gradient(linear, top, bottom, color-stop(0.00, #FFFF00), color-stop(1.00, #FF0000)); }

This code will create a vertical yellow-to-red gradient on the target element in supportive browsers. Each vendor-specific property value specifies a set of directional coordinates. Additionally, we’re specifying the type of gradient (linear) for the Webkit property. Instead of linear , we could also do a radial gradient:

.color-gradients { background-image: -moz-radial-gradient(top left, circle farthest-corner, #FFFF00, #FF0000); background-image: -webkit-gradient(radial, top, bottom, color-stop(0.00, #FFFF00), color-stop(1.00, #FF0000)); }

Notice the difference? Webkit specifies radial as a property value, whereas Mozilla prefers it in the property name itself. Weird, but true. Here’s how to get gradients working in IE :

filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#FFFF00, endColorstr=#FF0000); -ms-filter: "progid:DXImageTransform.Microsoft.gradient (GradientType=0, startColorstr=#FFFF00, endColorstr=#FF0000)";

Use GradientType=0 for vertical gradient and GradientType=1 for a horizontal gradient. Correct me if I’m wrong, but it may also be possible to specify an alpha-transparency value for each color, just as we did for the IE opacity fix.

Rotating Elements ↩

Rotating elements using CSS is like a dream come true for designers, who have spent years creating endless sequences of images and complex sprite configurations just to get something simple like displaying vertical calendar dates. Well say goodbye to all of that nonsense. Now with the amazing power of CSS 3, we can rotate any element as follows:

.rotate-this { behavior: url(-ms-transform.htc); -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg); }

Notice that we are increasing support by catering to browsers via their specifically prefixed selectors. The proprietary Opera support is new as of version 10.5, and that crazy IE behavior is explained in Samuli Hakoniemi’s article on cross-browser rotation transformation with CSS. How is this technique used as progressive enhancement? For central design elements, it’s not, but for peripheral embellishments such as calendar dates, it’s perfect. So long as your design accommodates the date text when it’s displayed horizontally, this technique will enhance the presentation for supportive browsers by rotating the design 90 degrees. Better still, as browser support improves, we’ll be able to use this — and the other methods presented in this article — consistently and reliably anywhere and for any purpose. Nice.

Ahhh yeh.

Make no mistake, CSS 3 brings plenty of awesome new features to the table. There are many new tricks and techniques to learn, and the time to begin practicing is now. By understanding and following the principles of progressive enhancement and graceful degradation, your web designs can take advantage of all that CSS 3 has to offer while providing practical and aesthetic benefit to your visitors.