Using CSS3 Wisely CSS3

Now that you’ve seen how useful and beneficial CSS3 can be, can you just plop it into your style sheets and get going? Well, yes, but we wouldn’t recommend it without learning some best practices first. Just as with CSS 2.1, CSS3 has its own special considerations that should go into how you craft your style sheets to make them more organized, efficient, and future-proof. You also need to know how to implement CSS3 in a way that won’t harm non-supporting browsers, and how to provide workarounds for those browsers when you so choose.

Browser Prefixes

When a browser implements a new property, value, or selector that is not yet at Candidate Recommendation status, it adds a prefix onto the front of the property with a code for its rendering engine. For instance, -moz-border-radius is the version of the border-radius property currently used by Mozilla-based browsers, like Firefox.

Table below provides a list of the available prefixes.

Browser-specific prefixes for CSS properties

Browser-specific prefixes for CSS properties

WHY THEY EXIST

Vendor prefixes allow browsers to try out new properties, values, and selectors before they are finalized—a good way for them to be tested in the wild, and then corrected and refined if necessary. If the browser were to jump straight to the unprefixed, standard property, they would be locked into whatever behavior they originally use.

Developers would start using the unprefixed property immediately, and would expect it to continue producing the same behavior from that point onward. If the browser changed the property after this point, either because its implementation was buggy or the specification itself had changed, it would risk breaking all the existing sites that had already started using the property. Not only does it lock the browser into its first, buggy implementation, it pressures the other browsers and W3C to follow suit. Eric Meyer gives two real examples of how this unfortunate cycle has happened in the past in his excellent article “Prefix or Posthack”.

Even if the browser didn’t change its implementation so as not to break existing sites, what if the W3C changed the specification?

And what if other browsers started using the new behavior described in the updated specification? Now you have different browsers displaying one single, standard property in different ways. That’s exactly how it used to be in the days of Netscape 4, Mac IE 5, and Windows IE 6.

Complicated and unstable hacks, based on bugs completely unrelated to the actual properties they meant to fix, proliferated because non-standard browser behaviors weren’t quarantined in browser-specific properties.

A prefixed property indicates to developers that the property is somewhat experimental and subject to change. It gives the browsers flexibility to continue making changes if necessary, which allows the browsers to release and refine new properties more quickly. This, in turn, gives developers the opportunity to use new properties sooner and participate in the refinement process through testing in realworld scenarios.

Once the specification has become more stable and the browser has achieved a correct implementation of the property, it can drop the vendor prefix. If the developer also had the non-prefixed version of the property in her styles—which is wise for future compatibility—her pages would now be able to automatically take advantage of the finalized behavior of the property. If she didn’t have the non-prefixed property, there’s no harm done—the old prefixed property will continue to work in the same way as before. None of the sites using the prefixed version of the property will break.

PROBLEMS WITH PREFIXES

Browser prefixes do have a few disadvantages, though. The chief complaint leveled against them is that you often end up with a few lines of CSS that all accomplish the same thing, such as:

This repetition adds to the file size of your style sheets and can be just plain annoying. It would be so much cleaner and nicer to have a single line using the standard property. Many CSS preprocessor scripts allow you to do this, actually—just write the non-prefixed property and it creates the browser-specific CSS for you. Tools that can do this for you include Sass, LESS, and eCSStender, to name a few. But using scripting to remove the prefixes has a number of disadvantages itself. If a browser has a buggy implementation of a property, you can’t choose not to use that browser’s prefix but keep using the other ones. Nor can you use different values for various browsers to accommodate their slightly different renderings of the same property. Also, adding scripts may slow your pages down. Eric Meyer explains what might be most risky about this method:

By hiding the prefixed properties behind a processor, authors may forget that what they’re using is experimental and subject to change. Cognitively, they may start to treat what they’re using as settled and stable when it may be nothing of the kind.

Although the repetition of browser prefixes is cumbersome, the alternative of one standard property producing different behaviors in each browser as they develop their implementations, resulting in convoluted hacks to work around the inconsistencies, is far more annoying. Plus, as time goes on and support improves, you can remove the prefixed properties, making your style sheets cleaner, instead of having to maintain hacks in your sheets for years because of a non-standard browser behavior that has snuck into a non-prefixed property. Let me quote Eric Meyer’s article once more, where he talks about how the “pain” of prefixes is temporary:

It’s a little like a vaccine—the shot hurts now, true, but it’s really not that bad in comparison to the disease it prevents. And in this case, you’re being vaccinated against a bad case of multi-year parser hacking and browser sniffing. We suffered through that long plague once already. Prefixes will, if used properly, ward off another outbreak for a long time to come.

Another problem with prefixes is that they don’t validate. This isn’t a problem in and of itself—validation is just a troubleshooting tool, so if you know why you are getting an error for a prefixed property, you can just ignore that error and move on. But having a whole bunch of “benign” errors for prefixed properties mixed in with the others can make it harder to spot the errors you’re really concerned about. To ease both of these problems—the repetition and the lack of validation— some people separate out the prefixed properties into their own sheet. That way, the main style sheet is kept pristine and will validate (or at least expose only the “real” validation errors when you check it). But many CSS people (including me) are not fans of this solution.

First, it adds another HTTP request; this impacts performance far more than the few extra bytes that the prefixed properties would add to the main sheet. Second, it makes it easy to forget you are using the prefixed properties; since they’re to be used with more caution than regular properties, paying attention to them is essential. If a browser changes the behavior of one of its prefixed properties, you may neglect to update your rules accordingly. Or if you’re simply trying to figure out why something is behaving in a certain way, it make take you a long time to remember that old prefix style sheet and track down the culprit. So we’m sorry to say that filtering through the validation errors caused by prefixes is probably the lesser evil compared with keeping a separate style sheet for prefixed properties.

Despite these disadvantages, most CSS developers are glad that prefixed properties are available and agree that their benefits, explained earlier, make them worthwhile to use in appropriate situations.

THE PROPER WAY TO USE BROWSERSPECIFIC PROPERTIES

When you use prefixed properties, you should always include the non-prefixed property as well, and always after all the prefixed versions.

This ensures that when the browser supports the non-prefixed property, it will use it, overriding the prefixed property listed earlier and using the newer, more correct behavior.

For instance, until the release of Safari 5, Safari used the -webkitborder- radius property. And it was a good thing it did—its implementation was incorrect in a couple ways (or rather, it became incorrect as the W3C refined the spec). For one thing, Safari 4 and earlier didn’t allow you to round each corner independently in the -webkit-borderradius property, as the specification says you should be able to. It also used incorrect syntax for specifying the curve of elliptical instead of perfectly rounded corners.

But this was OK. You could keep the incorrect syntax contained in the -webkit-border-radius property, unseen by any non-Webkit browsers. And by including the standard border-radius property last, containing the correct syntax, you could take advantage of the improved implementation of Safari 5 as soon as it was available, without having to make a single change to your style sheets. The standard property was already there, just waiting to be used.

While including the standard property last is almost always advisable, there are some rare times when we think you should leave it off entirely, and just use the browser-specific versions. If it looks like the syntax is still going through significant changes, we would advise waiting to include the standard property until it becomes more stable. There’s no point in including it if it’s just going to be ignored or break when the specification is finally firmed up and browsers start using the standard property.

A great example of this is CSS3-generated gradients. This may make you decide against using gradients entirely—but on the other hand, it’s a purely visual effect that degrades without a hitch in non-supporting browsers, and perhaps you’re going to use it only on an experimental, personal, or single-browser site like an iPhone app. If you do decide to use gradients despite the possibility of later syntax changes, the safest course of action is to use the prefixed versions only. But these cases are rare, partially because browsers don’t usually make a prefixed version of a property until the syntax is pretty well fleshed out, and also because even in those cases where they do, you’ll usually want to wait for more stable syntax.

Another optional guideline to follow when using browser prefixes is to always preemptively include all possible prefixed versions, even if some are not being used, on the chance that later they will be. We are neither absolutely for nor against this policy—for me, it depends on the situation. If we are working on a site that we’ll have to hand off completely and never touch again, we may think it best to include all possible vendor-prefixed properties. But if we’m going to be working on the site continually, we may find it most efficient to include only the prefixed properties we need now, and add others later if browsers start supporting them. You can do it either way.

No matter which prefixed properties you choose to include, it’s a good idea to put comments in your CSS indicating which property is used by which browser. It’s not always as obvious as you might think.

By including these comments, it makes it easy to later remove the properties you no longer need when you decide to drop support for a particular browser.

Dealing with Non-supporting Browsers

There’s no single way that you ought to handle browsers that don’t support the CSS3 you’re adding. Again, the route you take depends on what’s best for the site, your users, your client, your own personal preference, and CSS3 technique itself. We’ll discuss a few different routes you can take with non-supporting browsers, and here we’ll use them all at various points, each when appropriate.

ACCEPTING THE DIFFERENCE

In many cases, the best way to deal with browsers not supporting some of your CSS3 is to just accept the different visual appearance. That’s what progressive enhancement is all about, after all, and in a few cases, you have no choice, as there’s really no replacement for the CSS3 version. But even in those cases where you do have a choice, you have to ask yourself if the time you take creating a fallback method for non-supporting browsers is really worth it. Will it really help the users? Will it improve sales or newsletter signups or whatever the goal of the site is? In some cases, the answer will be yes—so go ahead and use an appropriate workaround. But in many cases, the answer is no, as the CSS3 effect is a non-essential, cosmetic enhancement. And in some cases, adding the workaround will actually make things worse for your visitors, as adding back images in older browsers may slow the page down, for instance.

Most CSS3 effects will not harm non-supporting browsers if they’re not seen. An example of this is the Twitter site. Twitter’s site uses border-radius to create rounded corners at variousplaces throughout the design, as well as other CSS3 effects that aren’t seen in IE 8 and earlier. In up-to-date, non-IE browsers, the “What’s happening?” box, where you type your tweets, has rounded corners, plus a blue glow around it when you place your cursor inside it . In IE 8 and earlier, the box simply has straight corners and no glow . There’s nothing broken or wrong about this appearance—it’s just different. That difference isn’t harming IE users at all, so there was no need for Twitter to provide workaround techniques to emulate the same appearance.

Twitter’s tweet box has rounded corners and a blue glow in Firefox.

Twitter’s tweet box has rounded corners and a blue glow in Firefox.

IE 8 doesn’t see the rounded corners or glow, but there’s nothing broken-looking or ugly in its alternative appearance

IE 8 doesn’t see the rounded corners or glow, but there’s nothing broken-looking or ugly in its alternative appearance

But there are times when failing to provide a fallback can make things worse for non-supporting browsers. For instance, if you make a background color semitransparent using HSLA or RGBA—two new ways of declaring color in CSS3—browsers that don’t understand these types of color values will have no color to display, and will make the background completely transparent. Depending on the text color and the color of whatever is now showing through that transparent box, the text may be completely unreadable . This is not one of the situations where you can just accept the difference. You need to provide a workaround.

FIGURE: In Firefox (left), the box has a semitransparent background, but in IE 8 (right), no background appears, making the text unreadable.

In Firefox (left), the box has a semitransparent background, but in IE 8 (right), no background appears, making the text unreadable

So, as with lots of things in CSS, it requires testing in multiple browsers to determine what the best course of action is. Often you can be all zen and accept the difference, but sometimes you can’t.

PROVIDING A NON-CSS3 AND CSS3 VALUE FOR A PROPERTY

In cases where you want to or must provide a fallback, you can sometimes do so simply by providing more than one value for a property in the same rule: the first one for non-supporting browsers, and the second, CSS3 one for more advanced browsers. Non-supporting browsers will ignore the rules they don’t understand, and CSS3-capable browsers will override the older values with the newer values. For instance, in the case of the nonexistent background color mentioned above, you can provide a solid fallback color in hex notation first, then the HSLA or RGBA version, like so:

Note that a method like this rarely actually emulates the appearance or behavior of the CSS3 property—the fallback color here is solid, not semitransparent. But it provides an acceptable second-choice appearance when doing nothing at all would make the page unusable for users of non-supporting browsers.

USING MODERNIZR TO DETECT CSS3 SUPPORT

When you want to use two different values to target non-CSS3 and CSS3-supporting browsers, it’s not always possible to include both values in the same rule, as we were able to do with the background color above. There are times when the two values would clash. Or maybe the two values don’t clash, but you want to provide completely different and more extensive fallback styles for the older browsers, and you don’t want the CSS3 browsers to read and use them.

You could get into browser sniffing, where you use programming to detect which browser a visitor is using, to create different rules for different browsers, but that’s unreliable and messy. A better solution is the script called Modernizr, available at www.modernizr.com. It detects whether the user’s browser supports a number of CSS3 and HTML5 features. Modernizr then adds classes to the html element that indicate which it does and doesn’t support, such as “no-multiplebgs” if the browser doesn’t support having multiple background images on a single element and “multiplebgs” if it does.

With these classes in place, you can easily write styles for each class and be sure that each rule will be seen only by browsers that do (or don’t) support the piece of CSS3 or HTML5 you’re concerned about. The following rules could be used to apply different background colors or images to browsers based on whether or not they support multiple background images:

The first rule is seen by all browsers, whether or not JavaScript is enabled and whether or not they support CSS3. Browsers that don’t support multiple backgrounds will use the background color, and browsers that do will use the three background images. The next rule is seen only by browsers that don’t support multiple backgrounds and do have JavaScript enabled. It feeds these browsers a single alternative background image in place of the three separate ones it wasn’t able to use. So no matter what level of CSS support the browser has, and whether JavaScript is available or not, each browser gets a background on the wrapper div.

For the most part, Modernizr is best for providing alternative (rather than emulating) styles to non-supporting browsers. But there are times when you could use it to emulate the CSS3 behavior or appearance. For instance, if you wanted to round the corners of a box, you could use border-radius for some browsers, and then use a background image of rounded corners for browsers that don’t support border-radius:

USING JAVASCRIPT TO EMULATE CSS3

So far, the workarounds we’ve gone over mostly provide an alternative style to the non-supporting browsers, instead of emulating the CSS3 behavior. In most cases, alternatives are fine. But if you need to have a more consistent appearance between the two, you need to emulate.

JavaScript can often be put to work to make non-supporting browsers do the same thing that CSS3 makes more advanced browsers do. For instance, for years now there have been scripts available for creating rounded corners.

I E FILTERS

Another way to emulate CSS3 without using JavaScript is to use Microsoft’s filters in your CSS to create a variety of visual effects. These only work in IE, of course, and they’re applied via its proprietary filter or -ms-filter property. The syntax for the value of the filter property partially depends on the specific filter being used, but the basic format is filter: progid:DXImageTransform. Microsoft.filtername(sProperties), where “filtername” is the name of the filter and “sProperties” is its value. In IE 8, the syntax was updated to -ms-filter as the property name, and you’re supposed to put quotation marks around its value.

Here are the filters that can be used to emulate CSS3 effects:

The DropShadow, Shadow, Glow, and Blur filters can emulate box-shadow and text-shadow.
The Gradient filter can emulate RGBA, HSLA, and linear gradients.
The Alpha and BasicImage filters can emulate opacity.
The Matrix and BasicImage filters can emulate 2D transforms.
The nice thing about filters is that they work without JavaScript and only in IE, without any need to hide them from other browsers, making them simple to apply. But they do have several disadvantages to be aware of:

Length. It takes a lot of characters to write a filter. If you use a lot of filters in a single sheet, you could increase its file size significantly. To combat this, you could place the filters in an IE-only style sheet, fed to IE with conditional comments, as you’ll learn about in a moment. That way, at least the browsers that don’t need them don’t have to download the extra bytes.

Invalid CSS. Your style sheets won’t validate if they contain filters. This isn’t really a problem as long as you understand why they’re not validating. But if it bothers you, you can place the filters in an IE-only style sheet so that at least your main style sheets validate.

Performance. Filters can make the page load slowly and use up a lot of memory.
Jagged text. Filters can turn off ClearType rendering in Windows so that text is no longer anti-aliased, making it look jagged.
Other bugs. Occasionally, filters will trigger other bugs in IE.
Because of these problems, we recommend you use filters only when you really have to. When you do use them, do so sparingly, and test thoroughly.

<!--[if IE]><link rel=”stylesheet” href=”ie_all.css” type=”text/css”><![endif]-->

Within this style sheet you can then use hacks to feed rules to different versions of IE, if necessary. However, with IE 9 coming out soon and having much better standards support, you probably want to make sure that version doesn’t use your IE hack sheet. To avoid this, structure your conditional comment so that it targets only IE 8 and earlier, using this syntax.

<!--[if lte IE 8]><link rel=”stylesheet” href=”ie_lte8.css” type=”text/css”><![endif]-->

The lte part of the conditional comment stands for “less than or equal to.” Other possible values are lt for “less than,” gte for “greater than or equal to,” and gt for “greater than.”

Instead of using just one IE sheet, another option is to use multiple conditional comments to feed a different style sheet to each version of IE you need to fix, like this:

This avoids the need for having hacks in any of the style sheets, but it may be a little harder to maintain.

HIDING FROM IE
Conditional comments can also be used to hide content from IE, not just feed it content. These are called downlevel-revealed conditional comments (though it’s not a very helpful name). The syntax looks like this:

The exclamation mark in front of IE tells all versions of IE that they’re not to use anything until they see <![endif], which closes the conditional comment.

This time, all other non-IE browsers do see the HTML between the conditional comments, because the beginning and closing conditional comments are actually each a standalone, regular HTML comment.

Here’s what non-IE browsers essentially see:

<!-- stuff that doesn’t concern me, and now the comment is over and I should start parsing again --> HTML outside of the comments. IE ignores it only because it’s been programmed to do so with its special syntax.

You can also use downlevel-revealed conditional comments on specific versions of IE, like this:

ADDING IE-VERSION CLASSES ON THE html TAG
Another method of using conditional comments is not to use them to feed IE its own style sheets, but to add classes to the html tag that indicate what version of IE is in use. Then, you simply write rules for each of these classes in your main style sheet. This technique isn’t as common as other conditional comment methods, but it’s been gaining in popularity since Paul Irish blogged about it in 2008

Here’s what the HTML could look like:

We know this looks rather overwhelming, but it’s really quite simple if you walk through it line by line. Each line is simply being read by a different version of IE and spitting out a different html tag. For instance, when IE 6 loads the page, it sees the conditional comment for lt IE 7, says “Hey, I’m less than 7! I’m going to apply the stuff inside this conditional comment!” and spits out the HTML <html class=”ie6” lang=”en”>. IE 6 then gets to the next conditional comment and sees that it doesn’t apply to itself, so it skips it. And so on down the page. This happens in every version of IE, so each gets only one html tag, and each html tag has a class on it that identifies which version of IE is in use.

Non-IE browsers ignore all conditional comments, so they ignore the first five lines. The sixth line uses a downlevel-revealed conditional comment, so IE doesn’t use it and non-IE browsers do. Thus, non-IE browsers apply only the last line with a plain, class-less html tag.

Once you have this HTML in place, you can create rules for any version of IE you wish directly in your main style sheet, removing an HTTP request and keeping your rules all in one place for easier debugging and maintenance. You don’t have to use hacks, and you don’t have to worry that non-IE browsers might see something they shouldn’t. For instance, if you wanted to feed IE 6 a height value to make up for its lack of min-height support, you could simply do this:

While including IE-specific rules in your main style sheet does add to its size, the increase should be minimal—hopefully, you are writing CSS that avoids IE bugs in the first place instead of getting littered with hacks. Plus, remember that HTTP requests are far more expensive in terms of page performance than an extra kilobyte or two, so overall this technique should be more efficient, both in terms of page performance and of your development and maintenance process.

Because of these advantages, I like this last option for filtering IE the best, so it’s the method we’ll be using here. Feel free to skip the html classes and separate IE rules out into their own sheets instead if that’s your preference.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

CSS3 Topics