Chapter Twelve: CSS3 and the Future of CSS

CSS Reloaded

The specifications for CSS1 and CSS2 emerged from the World Wide Web Consortium (W3C) in quick succession in the latter half of the 1990s. By the early years of the following decade—and particularly with the 2001 release of IE6—browser makers and web developers had widely adopted and implemented the entirety of the CSS1 specification, along with the positioning aspects of CSS2. Bad development practices such as the use of presentational HTML elements such as <font> and of tables for page layout have nevertheless persisted, but they continue to decline as the message of web standards permeates the web development profession.

After 2001, the browser landscape largely stayed the same for several years. Internet Explorer 6 for Windows—which when released was considered by most to have first-class support for CSS—came to dominate, while Netscape essentially vanished, and Mozilla, Netscape’s open-source successor, seemed to flounder. The Mac platform also languished in the early part of the decade. As a result, IE6 became the de facto standard for many developers. Left untouched by Microsoft for several years, IE6 and its bugs became familiar, and workarounds for those bugs became part of the standard web development toolkit. Many websites, designed to be “best viewed in IE6,” broke in browsers that supported standards rather than IE6’s idiosyncratic way of doing things. Perhaps most discouraging, browser makers stopped implementing new CSS features, while development of the CSS3 standard stagnated.

Innovation on the web, at least in terms of CSS, appeared to be over.

With the release of Apple’s Safari in 2003 and Mozilla’s Firefox in 2004, coupled with the resurgence of the Mac platform, web users gained access to options other than Internet Explorer on Windows. Both Safari (which was based on the open source KHTML) and Firefox demonstrated a strong support for web standards, and the makers of both browsers had a vested interest in using innovation to challenge Microsoft’s dominant browser.

Enter CSS3

The W3C is currently developing two CSS specifications. The first, CSS2.1, is designed to be a “snapshot” of CSS usage and consists of all CSS features that are implemented in all modern browsers at the date of its final publication. Rather than focusing on innovation, this specification provides a stable baseline of features that are available in all major browsers. The second specification, CSS3, is the hub of innovation in the feature set of CSS. Rather than being a single, monolithic standard like CSS1 and CSS2, CSS3 consists of modules, such as the Selectors module and Fonts module, each of which has different editors and authors as well as varying levels of priority and activity. The modular nature of CSS3 means that the W3C can develop parts of the specification independently from the rest, and that it can easily incorporate new modules, such as the recently added CSS Transitions and CSS Transforms modules.

Although the standards process has been improved, significant challenges remain. How can newer browsers support features of CSS3 not supported in other browsers (particularly IE6) without breaking the web all over again? How can they responsibly implement experimental, unfinalized CSS features? Is it possible to have the stability of a standards-based web and the innovation that browser competition brings? In the rest of this chapter, we’ll explore the ways in which a combination of clever standards development, smart browser implementation, and the judicious use of new CSS features by web developers has put to rest the argument that you can’t have innovation within the context of standards—and we’ll see how to implement designs like the following navigation menu using CSS alone.

To understand what CSS3 is capable of, consider an implementation of the Apple.com navigation bar [12.1] that uses no images and no extraneous markup—just a simple list and CSS. In this chapter and the next, we’ll cover all the selectors, properties, and other features of CSS required to implement this navigation bar.

12.1 A navigation bar like Apple’s, created with CSS3 and HTML (no images), as rendered in Safari 4 in this unenhanced screenshot

image

Backward and Forward Compatibility “Solved”?

Backward and forward compatibility pose two related but quite different challenges for web developers who want to use features of CSS3. When developing with these features, we want to ensure that users of browsers that don’t support them still have access to the information and services our sites offer. But we also want to provide an improved experience for users of more capable browsers. As noted in earlier chapters, the principles of “progressive enhancement” (the practice of adding additional sophistication to the appearance of a page for users of modern browsers) and “graceful degradation” (the idea that when a browser doesn’t support a feature, the consequences of that lack of support should have as little impact on the user as possible) will be vitally important to the intelligent use of CSS3 features.

The key to meeting these challenges is to be mindful of the consequences of a lack of browser support for the features we elect to use. Testing with browsers that don’t support a particular feature will, of course, be an invaluable tactic. But it’s even more important to consider, as early as possible in the design phase, what happens to a page’s usefulness if a particular feature is missing. The absence of text shadows, for example, can affect the legibility of information on a page [12.2, 12.3].

12.2 A heading that relies on a text shadow to maintain legibility.

image

12.3 The same heading as displayed by a browser that doesn’t support text shadows. Always ensure sufficient contrast between text and background colors.

image

On a related note, we must ensure that if a browser supports a feature in a buggy or otherwise unsatisfactory way, it won’t try to use that feature in its rendering of a page. Since the inception of CSS, buggy and incomplete support has caused more problems than the absence of support for a particular feature. The problem of buggy support was initially worsened by the fact that until recently, developers had to choose between using each feature either in all browsers, or in none of them. As we saw in earlier chapters, a cottage industry of hacks and workarounds has grown up to allow developers to hide CSS from individual browsers. (A classic example of this is the box model hack we examined in Chapter 7.) Fortunately, there is now a standardized, widely supported way of targeting specific browsers: vendor-specific extensions.

Vendor-Specific Extensions

Vendor-specific extensions are prefixes added to a property name, selector, or other CSS feature. They specify that only browsers that support a particular extension should use the feature to which they’re appended. When you are confident that a feature you want to use is well supported in a particular family of browsers, you can use the feature with the appropriate extension, and rest assured that only the specified browser family will try to use the feature. Once a feature has become part of a published CSS standard and a specific browser developer considers their browser’s support to be sufficiently robust, the browser will also recognize the feature without the extension. Until then, in order to use such features, developers must explicitly declare which browsers should use them by adding the appropriate extension.

There are three such extensions in common use:

-webkit- for WebKit-based browsers (for example, -webkit-box-shadow)

-moz- for Mozilla-based browsers (for example, -moz-border-radius)

-o- for Opera-based browsers (for example, -o-text-overflow)

In addition, there are three much less commonly used extensions:

-ms- for Internet Explorer

-khtml- for older versions of WebKit and the Konquerer browser for Linux

-wap- from the WAP Consortium

So how does it work in practice? The border-radius property of CSS3 enables you to specify whether the corners of an element should have rounded borders; we’ll look at the property in more detail shortly. For some time, border-radius was well supported in Safari but not nearly so well supported in Mozilla browsers, and not at all by other browsers. If vendor-specific extensions didn’t exist, we’d have had to either use border-radius in Safari- and Mozilla-based browsers—knowing that this feature would look far from sophisticated in Mozilla browsers—or to not use it at all. Because both Mozilla and WebKit browsers support vendor-specific extensions, however, we can specify the -webkit- but not the -moz- extension, ensuring that the property is used only by WebKit-based browsers. When using vendor-specific extensions, it’s best to also specify the standard form—in this case, border-radius—as well, when such a form exists. For example, in our border-radius case, we’d use something like this:

image

Providing the standard property name ensures that when future browser versions support a feature fully, we won’t need to revisit our code. We can rely on the fact the browsers should only support a feature without the browser-specific extension once the feature has become standardized and once that browser supports it fully.

Firefox 3 now fully (and correctly) supports border-radius, though still only with the -moz- extension, and because Firefox 2 has a rapidly diminishing user base, we might now decide to use border-radius in Firefox. So, in addition to the -webkit- and standard versions, we might choose to add the -moz- version:

image

This will give rounded borders to all paragraphs in any WebKit- or Mozilla-based browser, and in any browsers that support the property once the CSS3 module specifying border-radius is finalized in the future—perhaps in Opera in 2010, in Internet Explorer 8.1, or in an entirely new browser.

For developers who missed out on the browser wars of the 1990s, having a simple, standardized way of targeting specific browsers may not seem all that significant. But I’d argue that one reason for the resurgence in CSS innovation is precisely our ability to pick and choose CSS3 properties based on their support in today’s browsers.

Now that we’ve seen how CSS3 is being designed and implemented in browsers in such a way as to be usable by developers today, let’s dive into some of these new features of CSS3. Rather than exhaustively cataloging all new features, we’ll focus on a number of selectors and properties in CSS3 that have good support in one or more modern browsers: features that are useful today. And throughout, we’ll focus on progressively enhancing the experience of users with browsers that support advanced features, while ensuring that users of older or less sophisticated browsers retain access to the information and services our pages provide.

New Selectors

One key to true web development proficiency is the intelligent use of CSS selectors. In Chapter 4 of this book, we looked at selectors such as the descendant and child selectors—selectors often underutilized or ignored by developers but which provide tremendous power when used with care. These selectors can be particularly useful when the HTML for a particular page is off-limits—for example, when the markup is generated by a CMS or other application. CSS3 provides even more powerful and finely tuned selectors that are already widely supported in browsers, even though the CSS3 Selectors module is still a candidate recommendation rather than a final, published standard. All current, popular browsers except for Internet Explorer 8 and its ancestors support most or all CSS3 selectors. We can, therefore, use CSS3 selectors today, though we must take care to ensure that our sites are still useful when viewed in Internet Explorer.

In the following pages, we’ll look at a number of CSS3 selectors that can be useful in common development situations and which can be used for progressive enhancement. In the next chapter, we’ll follow up by looking at new properties in CSS3.

Sticky Note

There are a number of JavaScript libraries, such as Dean Edward’s IE7 (dean.edwards.name/IE7) and John Resig’s Sizzle (sizzlejs.com/), that provide ways in which you can use CSS3 selectors with Internet Explorer 6 and upwards. These libraries do, of course, require users to have JavaScript enabled in their browsers.

Structural Pseudo Element Selectors

As we saw in earlier chapters, a good understanding of descendant and child selectors can help alleviate the symptoms of “id-itis” and “class-itis,” ailments characterized by specifying class and id values for elements in an HTML document solely for the purpose of styling those elements. CSS3 provides even more fine-grained selectors for selecting elements based on the structure of a document than its predecessors do. For example, we might want to select every other row of a table, or only the first paragraph inside each parent element, or only the last item in a list. Structural pseudo element selectors in CSS3 give us the ability to do precisely these things.

Document Structure

We saw earlier that we can think of the structure of an HTML document as a tree—a little like a family tree, though with only a single parent per element. Perhaps a better analogy is the classic “tree of life” way of imagining evolution. At the top of the tree is the HTML element: the root element of the document. Every other element:

• Always has exactly one parent element—for example, the body and head elements have the HTML element as their parent.

• May have one or more child elements (elements contained directly within them)—for instance, the body and head elements are children of the HTML element.

• May have one or more sibling elements (elements that share a parent)—the body and head elements, for example, are siblings, as they have the same parent: the HTML element.

• May have descendent elements (child elements of an element’s child, or an element’s child’s child, and so on)—for example, every element in an HTML document is a descendent element of the HTML element, and all headings, paragraphs, links, and so on, are descendants of the body element.

first-child

In print design, the first paragraph is often styled differently from subsequent paragraphs in a chapter. It’s not hard to style a web page using CSS in this way by applying a class value to such paragraphs. For example, using a little of the HTML5 we picked up in the last chapter, we might do the following:

image

Now, in the example of a classic such as Pride and Prejudice it is rather unlikely that the first paragraph of a chapter might need moving elsewhere, but many other kinds of documents are far more dynamic—articles in the process of being edited, for example, or blog posts, or developing news stories. As a consequence, during the development or maintenance of a site, a paragraph might easily be copied elsewhere with the first-paragraph class intact, which would mean that the new “first” paragraph wouldn’t be styled correctly. In cases like this, it would make a lot more sense to apply a style based on an element’s place in the structure of the document. This is a far less fragile solution than marking up the document with classes, and the first-child selector is designed for precisely this kind of situation.

Syntax

Using the first-child selector is very straightforward. We simply append first-child to a selector that identifies the element we wish to select. For example, p:first-child selects any paragraph that is the first child of its parent element. We might use this selector to, for example, make the text of first paragraphs bigger than following paragraphs, using a rule that looks like this:

image

Note that this example selects all paragraphs that are the first child of their parent elements, not the first child of all p elements. (You can think of the first-child selector as being similar to a dynamic selector like a:hover, except that instead of selecting an element when it is in the hover state, it selects an element when it is the first child of its parent element.) Note also that if we hadn’t specified any element before the first colon in this example, this rule would have selected all elements that are the first child of an element. We’re unlikely to want to indiscriminately select all first child elements, but if we needed to do so, that’s how we’d do it.

We can also append first-child to other selectors. For example, we can select any element with a class of “chapter” that is also the first child of its parent element, like so: .chapter:first-child. We can also combine first-child selectors with, for example, descendant selectors, thus selecting only paragraphs that are the first child of section elements with the class “chapter.” Here’s what that looks like:

image

first-of-type

Now, the preceding example may seem a little artificial, because you’d probably expect a section element to contain a heading that comes before the chapter’s contents. A more realistic example might look like this:

image

In this case, there is no element that matches the selector p:first-child, because the first paragraph in the section is preceded by a heading. If we want to style the first paragraph element of every section that has chapter as its class, we’ll have to turn elsewhere. CSS3 does offer a more general selector than first-child: the nth-child selector works in a similar way, but although we’ll examine this selector in detail shortly, it’s still not the best match for this situation. If, for example, we used nth-child to select paragraphs that were the second child of their parent elements, we would still have to rely on a stable document structure. If one or more chapter sections had a different structure, or if we changed the whole document’s structure during development, our solution would need to be changed once more.

Less fragile is another CSS3 structural selector, first-of-type. This selector allows us to select an element when it is the first instance of the specified type of element within its parent element. In this case, first-of-type is the selector we want, as it will allow us to select only the first paragraph inside an element, whether that paragraph is preceded by other elements (like headers) or not. Our rule will look something like this:

image

This is a much more robust solution than adding class to the markup or relying on a specific document structure. If we move a paragraph elsewhere in a section or document, or if the structure of the parent element’s content changes, our solution will still work. And unlike class- and id-based solutions, the first-of-type selector won’t require us to touch the document’s HTML at all. Looking for the least fragile selector pays long-term dividends in the form of easier code maintenance and greater reliability.

How can we decide whether a solution is fragile or robust? It’s more an art than a science, but fragile solutions will tend to be inflexible, to rely on a specific document structure, or to require that we change the document’s markup to accommodate styles, usually by adding class or id values. Robust solutions often allow us to select elements based on characteristics that don’t require a specific, rigid document structure. CSS3 provides sophisticated selectors, and it pays to consider them before falling back on methods like using class and id for styling. Anytime you find yourself changing the markup of a document to accommodate CSS, alarm bells should ring. We have more appropriate tools than the hammer for adding to our markup, and if you do alter markup, it should be as a last resort.

As is the case with all CSS3 features, we should use these selectors to progressively enhance a page’s presentation. In the preceding example, there’s no great loss for the users of browsers that don’t support these selectors—the only thing those users will miss is a slight change in type size for the first paragraph of each chapter.

last-child

The selectors we’ve seen so far select elements based on their order beginning with the first child of the parent element and counting forward, but we can also select elements based on their order beginning with the last child and counting backward. Just as first-child selects an element when it is the first child of its parent, last-child selects an element when it is the last child of its parent element. Similarly, last-of-type selects an element when it is, for example, the last paragraph inside its parent element.

Such selectors are useful for selecting, say, the last paragraph within a chapter section, perhaps in order to add additional margin space, or a border. The following example applies the solid thin black border to paragraphs that are the last child of a section with the class chapter:

image

More robust, however, is the last-of-type selector. If the structure of our section changes such that an element other than a paragraph becomes the last element, the last paragraph of the chapter will still be selected—and thus still have a border—if we use last-of-type in our CSS rule:

image

So far, we’ve been selecting specific, individual children of an element, but we can also select groups of elements that meet specific structural criteria within an element. We’ll turn to these selectors next.

nth-child

One of the most common ways of styling data tables, both in print and on the web, is to alternate the background colors of rows of data—a technique sometimes referred to as zebra striping. Typically, zebra stripes are implemented in HTML and CSS by applying a class such as odd or even to every other row in the HTML markup, then using a class selector to apply a background color to odd, even, or both classes of row. This method violates our rule of changing markup as little as possible for the purposes of styling, but until CSS3, there was no other way—short of using JavaScript—to implement zebra stripes without markup changes. As you might guess, CSS3 provides a selector that solves our problem.

CSS3 introduces the nth-child selector, which selects an element when it is the 3rd, 17th, or any other specified child of an element. We need a selector that will allow us to select every other row in a table, and luckily, we can use nth-child to do this in several ways.

In addition to using nth-child with numbers such as 1 or 27, we can use two keywords: odd and even. As you might guess tr:nth-child(odd) selects every odd-numbered table row, and tr:nth-child(even) selects every even-numbered row. We might use the following rule:

image

...which would produce a table that looks something like this:

12.4 Zebra striping a table with nth-child

image

We could also specify two alternating background colors by using both odd and even nth-child selectors:

image

...which would produce a table that looks something like this:

12.5 Two-color zebra striping in a table using nth-child

image

But what if we wanted to use three different background colors? This is where another feature of CSS3 structural selectors becomes useful. We can use a formulation like 2n, 3n+1, or 5n+2 to select groups of child elements, based on their position within their parent element. For example, tr:nth-child(2n) selects all the even children of an element, and tr:nth-child(2n+1) selects every odd table row. You can think of n as a variable that is replaced with every integer from 0 on. So tr:nth-child(2n) will select these rows:

tr:nth-child(0) — (2*0)

tr:nth-child(2) — (2*1)

tr:nth-child(4) — (2*2)

tr:nth-child(6) — (2*3)

...and so on. If a table had 1,000 rows, the 1000th row would be selected by this selector, because 1,000=2*500.

The selector tr:nth-child(2n+1), on the other hand, selects all the odd children of an element:

tr:nth-child(1) — (2*0+1)

tr:nth-child(3) — (2*1+1)

tr:nth-child(5) — (2*2+1)

tr:nth-child(7) — (2*3+1)

The 999th row of a thousand-row table would be selected by this selector, because 999=2*499+1.

We can also use nth-child selectors to select, for example, every third element (tr:nth-child(3n)), the element that comes before every fifth element (tr:nth-child(5n-1)), and so on. Let’s see how can we use these selectors to give three different background colors to a table’s rows.

We’ll create one selector to select every third element, one to select every third element plus one, and one to select every third element plus two. The following three selectors will do the trick:

tr:nth-child(3n) — selects every third element

tr:nth-child(3n+1) — selects the first element after every third element

tr:nth-child(3n+2) — selects the second element after every third element

We can now apply three different background colors, repeated indefinitely, with the following CSS:

image

12.6 Alternating three-color stripes in a table with nth-child

image

To get a sense of just how sophisticated these selectors can be, let’s take a look at a real-world example [12.7].

12.7 Styling a table with first-of-type and nth-of-type, as displayed in Safari 4

image

As with zebra-striped table rows, before CSS3 selectors became available, we’d have had to use classes to style the various td elements in figure 12.7 with different background colors. Using CSS3 selectors, we can simply select the first td element in a row and give it a reddish background, select the second td element in a row and give it a blue background, and select the third td element and give it a grey background. Our CSS rules would look like this:

image

Because the td elements that span three columns (such as the registration row) are the first of their type in their row, unless we specify a different background color for these rows, they’ll also be red. So, the fourth rule, which has an attribute selector, selects td elements that have a colspan value and gives these elements a lighter grey background. (If you need a quick refresher, check out the discussion of CSS2 attribute selectors in Chapter 4.)

As always, we need to consider what happens if a selector is not supported by a user’s browser. In this case, the text color of the session names could be very similar to the background color of td elements, which would compromise legibility. We can rely on specificity (which we discussed in Chapter 4) to help us avoid this problem. A suffix such as nth-child increases the specificity of a selector, which means that td:nth-of-type will override any plain td selector. So, we can safely use something like the following CSS rule to allow for graceful degradation:

image

This rule ensures that browsers that don’t support our CSS3 selectors will give all our td elements a blue background, and browsers that do support the structural CSS3 selectors will override this rule and apply the more sophisticated styles illustrated in figure 12.7.

It’s a little-known but useful fact that the keywords odd and even, as well as formulations like 3n+1, also work for other structural pseudo element selectors, such as nth-of-type.

The structural selectors of CSS3 are among the most significant enhancements to CSS in this version of the language. We’ve touched on many of them here, and on some of CSS3’s extensions to the CSS2 attribute selector that we looked at in Chapter 4. We’ve also covered the most important concepts for understanding how these selectors work. You can find more resources for investigating CSS3 selectors in the resources section at the end of this book.

target

For a completely different new selector in CSS3, we turn to the target selector. Any web developer will be familiar with selectors such as link and hover, which along with focus, active, and visited are referred to as “dynamic”—or, to be exact, dynamic pseudo-class—selectors. The target selector is an additional dynamic selector in CSS3. It selects an element when it is the element currently being pointed to by the document’s URL. For example, if the current URL in a browser’s address bar is http://westciv.com/index.html#resources, the element <p id="resources"> is the current target. (Of course, documents frequently lack a current target—for example, if the current URL is http://westciv.com/index.html, there is no target element.) When the current URL is http://westciv.com/index.html#resources, the target selector p:target will select the paragraph with an id of resources. Similarly, if we use target by itself, we can select whatever element is the target of its document (if there is a target). We can also chain selectors to make the target selector more specific. In the preceding case, if we want to select only the paragraph with an id of resources when it is the target, but not to select any other targeted element, we can use this rule:

image

So, what good is this selector exactly? When we jump to a location inside a page, either from within that page or from another page, it’s often not immediately clear what (or where) the linked element is. We can use the target selector and a visual cue, such as a particular background color, to draw attention to the target of the link and help the users orient themselves within the page. We might use a rule that looks like this:

image

In this example, whenever the user follows a link—from within or from outside a document—to any specific paragraph on our page, that paragraph will be styled with a yellow background until the target URL for the page changes.

The target selector is an example of the new features in CSS3 that are more focused on web applications than traditional web pages. In fact, there are a number of new properties specifically designed for working with web applications, some of which we’ll see in the next chapter.

The Wrap

In this chapter, we’ve taken a look at many of the new selectors in CSS3, along with examples showing they can be used to progressively enhance sites for users of browsers that support them. These selectors give us more power and finesse in selecting elements based on a document’s structure and help us continue to separate a document’s markup from its presentation. Thanks to these selectors and their increasingly broad support in contemporary browsers, we are approaching the day when class and id values applied solely for styling purposes will be a thing of the past. The benefits of this “cleaner” markup aren’t only theoretical: as more teams work with shared resources on version control systems, fewer markup changes mean more efficient collaboration—and when structural changes to the markup no longer affect our CSS, our sites become much easier to maintain.

Just as the intelligent use of descendent and child selectors is one of the hallmarks of the adept CSS developer, the understanding and use of new CSS selectors—especially structural selectors—will be vital skills for CSS developers for years to come.

But of course, selectors aren’t much use without properties. In the next chapter, we’ll take look at the really fun part of CSS3—the new CSS properties we can begin to use now.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset