- Applying CSS to the page
- Styling the basic and enhanced experiences
- Accessibility considerations
- Dealing with bugs and browser inconsistencies
Dealing with bugs and browser inconsistencies
Writing CSS that works consistently across browsers is often more difficult than it looks. Hacks—the "creative" syntax developers use to target or exclude a specific browser—should be used as a last resort, and applied selectively, because they're difficult to maintain and may cause rendering issues as newer, standards-compliant browser versions are released. Fortunately, hacks are usually avoidable if you approach CSS the right way. For the limited cases where it's unavoidable, we have some recommended techniques.
Conditional comments
Internet Explorer is infamous among web designers for its quirky handling of CSS, but given its share of the browser market, even minor rendering differences are hard to ignore. For situations where you simply can't make your layout render properly in versions of Internet Explorer, there are conditional comments—a proprietary feature of Internet Explorer that let you specify HTML markup that only IE will see. (Since they use standard HTML comment syntax, other browsers will disregard them.)
When CSS workarounds for IE are necessary, you can list those style rules in a separate style sheet and reference it on the page within conditional comments:
<!--[if IE]> <link rel="stylesheet" type="text/css" href="ie_fixes.css" /> <![endif]-->
Conditional comments can be targeted to a particular version of IE. We always recommend specifying a version, or all versions prior to a particular one, by using lt so later, more standards-compliant versions of IE won't see them. For example, the following conditional comment is read only by versions of IE released before version 7 (that is, earlier than IE 7):
<!--[if lt 7]> <link rel="stylesheet" type="text/css" href="ie_fixes.css" /> <![endif]-->
When writing CSS within IE-specific style sheets, we recommend the following:
- Target exceptions only: An IE-specific style sheet should contain a minimal number of workarounds that cascade off styles that are served to all browsers.
- Use filters sparingly: Many versions of Internet Explorer include proprietary CSS properties known as filters, which enable advanced effects such as shadows and blurs, and allow fixes for IE's rendering of semi-transparent PNGs (see sidebar). Unfortunately, IE filters are invalid CSS, and can also slow down page performance. Use with caution, if at all.
Common issues and workarounds
Through our experience in creating CSS layouts that work across a wide array of browsers, we've noticed a number of issues that seem to occur in almost every project. Here are some workarounds we commonly use to fix them.
Clearing and containing floats
When a CSS float is applied to an element, its height is no longer recognized by its parent element. This layout behavior sometimes conflicts with how the layout should render, so there's a need for a workaround to make the parent element wrap around its floated children. The simplest fix for the problem is to float the parent element, or to set its overflow property to auto, but this doesn't work in every situation.
As an alternate approach, we can apply a workaround known as "clearfix." The clearfix technique works by injecting an element just after the last floated child element, and using CSS to give it block and clear properties. Since this new element is not floated itself, it will cause the parent to recognize its location and wrap around it.
The CSS for clearfix is pretty clever. It uses the :after pseudo-element selector to create and style an element—in this case, a single character—without the need for JavaScript. Simply add the following style rule to your enhanced style sheet, and apply class="clearfix" to an element to force it to wrap around all floated child elements:
.clearfix:after { content: " "; display: block; height: 0; clear: both; visibility: hidden; }
The :after selector is not supported in Internet Explorer versions 7 and earlier, so it's necessary to include a second selector to ensure the clearfix technique works. The proprietary zoom: 1 property triggers hasLayout (described later in this chapter) in Internet Explorer, which will cause it to wrap its floated child elements. Keep in mind that it's good practice to store IE-specific styles in a separate style sheet and reference that style sheet using a conditional comment:
/* for Internet Explorer */ .clearfix { zoom: 1; }
There is an alternate, and less obtrusive, approach to assigning the clearfix class throughout the markup. The clearfix style rules can be scoped to all elements that need them by listing their selectors in the style sheet:
#header:after, div#primary-navigation:after, #primary-content:after, #footer:after { content: " "; display: block; height: 0; clear: both; visibility: hidden; } /* for Internet Explorer */ #header, div#primary-navigation, #primary-content, #footer { zoom: 1; }
This change keeps the markup free of classes for visual workarounds, and can reduce page weight.
Addressing z-index issues
In modern websites, it's common for user interface components to not only be positioned next to each other, but also stacked in front of or behind other elements. In CSS, this stacking is handled through a property called z-index.
The z-index property can be applied to any element with relative, absolute, or fixed position (assigned with the position property), and accepts a number that represents its vertical stack position among other elements on the page; an element with a higher z-index value appears in front of elements with lower (or no) z-index values. The entire document has a stacking context—elements are stacked relative to sibling elements on the page—and every container element with a z-index applied creates a new stacking context for child elements, meaning that child z-index values are relative to one another, and not to the rest of the page.
Internet Explorer versions 7 and earlier interpret this stacking model incorrectly: they create a new stacking context for all positioned elements, even those without a specified z-index. This can make it difficult to layer stacked elements in a predictable way. For example, an absolutely positioned tooltip that sits inside a relatively positioned fieldset element may appear behind other positioned elements on the page, because IE treats them like stacked elements when they should just appear in the document flow.
To combat this issue for absolutely positioned elements, we use JavaScript to append the markup (in this case, the tooltip) just before the end of the body element. Doing this ensures that the tooltip will sit in the document's stack order (not a particular element's), and if we assign a high enough z-index value, it will appear in front of all other page elements. In general, we recommend taking this approach when dynamically appending any overlaid content, because it removes any guesswork about where an element should be inserted to layer correctly above all other page content.
Another Internet Explorer z-index issue is that "windowed" elements such as select, iframe, object (used for plugins like Adobe Flash), and anything with scrollbars, all tend to stack in front of everything else, regardless of source order or z-index value. Unfortunately, the workaround for this issue is not pretty: we use JavaScript and CSS to inject an empty, hidden iframe element directly behind any content that is intended to appear in front of the rest of the page, with dimensions large enough to act as a shield, blocking windowed content from leaking through.
Fixing hasLayout bugs in Internet Explorer
When you test CSS layouts in Internet Explorer, you're bound to run into seemingly random rendering bugs—elements that appear partially visible, act oblivious to style rules, blink while scrolling, or reveal fragments of text in unexpected places on the page.
These types of bugs are often the result of issues with IE's rendering engine, and are typically caused by an element lacking a quality Microsoft refers to as hasLayout. The details of what exactly hasLayout means are somewhat mysterious. In general, block-level elements are considered to have hasLayout in IE's rendering engine, but sometimes the engine forgets to apply hasLayout where it should.
There are a few CSS properties that can be applied to an element to trigger hasLayout:
- position: relative; This property is often the safest hasLayout workaround; we generally try it first.
- zoom: 1; An invalid and proprietary, yet somewhat harmless, CSS property that assigns a page-zoom level of 100% to an element, rendering it as it would by default.
- height: 1%; or any height setting, for that matter, will usually trigger hasLayout.
To ensure that future browser versions with better standards support won't see these styles, consider placing hasLayout style fixes in an IE-specific style sheet that is targeted only to the specific versions of Internet Explorer that need them.
We've covered how to effectively write and organize your styles so they can be cleanly targeted to the basic and enhanced experiences. Next, we'll discuss how to transform the foundation markup by unobtrusively layering on JavaScript to enhance the page and add interactivity.