Stuff and Nonsense: Strategies for CSS Switching
Got something to say?
Share your comments on this topic with other web professionals
In: Articles
Published on July 18, 2005
We wish we had some kung fu–esque robes handy. This is the chapter where we tell our dear readers to forget all that we have taught them about CSS so far, to look beyond the surface of the pool and discover the truth within the truth . . . or something like that.
Honestly, we’re more fun at parties than we might seem.
After seven chapters, it’s worth remembering that as convenient as it is to site designers, cascading style sheets can also drastically improve the online experience for the users of our sites. In Chapter 2, you saw briefly how the cascade was written with our users’ needs in mind, that user style sheets are ultimately given precedence over the author style sheets we write. The authors of the specification didn’t do this to spite those that design for the Web, but rather to empower those who read it.
After all, the true wonder of the Web is its promise of universal access: an avenue through which a user can gain instant and complete entry to any topic, from anywhere in the world. In fact, much of the mantle we don as Web designers is to realize that promise—to make sites that are at once visually compelling and with an interface that presents no barrier to entry.
However, we’ve slowly come to realize that our understanding of our audience has been incomplete at best. While we focused on setting the type on our pages 9 pixels high in the early days of the Web, our development was focused on having sites “look right” on contemporary desktop browsers. But in recent years, our understanding of our users’ needs has matured. People with physical, hearing, visual, or cognitive disabilities have always been using our sites; it’s just taken us some time to realize it. So, it’s only in recent years that our definition of “accessibility” has flowered, and our site-building techniques have followed suit.
While some designers may tell you that building an accessible site means building a boring site, we’d fling their pooh-pooh right back at them. Accessibility isn’t about larger fonts and creating high-contrast guidelines. Some users of the Web can read only smaller texts, while others can see only yellow text on a black background. Rather, many of the design techniques explored throughout this book—semantic, well-structured markup, a separation between content and presentation—can and will afford us incredible leverage in building professional, inspiring designs and simultaneously improve the accessibility of our sites for all of our users, not just a select few. In short, we can better realize the Web’s potential for universal access, and make some ultra-sexy sites to boot.
Ultimately, this chapter is not a manifesto on accessibility, space allotments and our meager skills being the largest impediments. Instead, we will explore different techniques for democratizing our design through the use of style sheet switching. By applying a different CSS file to a markup document, we can drastically change any or all aspects of its design—the layout, typography, or color palette. This technique may hold incredible appeal to designers because it exponentially decreases the amount of overhead required to redesign a site. But, as you’ll see, this technique can wield incredible benefits to our site’s users, allowing them fine-grained control over a page’s presentation and, in turn, better access to the content therein. After all, it’s about throwing the gates as wide open as possible.
Let’s dive right in.
Laying the Foundation
As with other chapters, let’s begin with a valid XHTML document. For the purposes of our style sheet switching experiments, the document in Listing 8-1 will do nicely.
Listing 8-1: The Markup Foundation for Our Style Switcher Experiments
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Always offer an alternative.</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <div id="container"> <div id="content"> <h1>Always offer an alternative.</h1> <p><span class="lead">Lorem ipsum dolor sit amet,</span> consectetuer adipiscing elit. Nullam tortor. Integer eros...</p> <p id="blurb">This is, as they say, a “pull quote.”</p> <p>Donec id nisl...</p> <h2>Additionally, you might consider...</h2> <p><img src="portrait.png" alt="An author's handsome (if pixellated) mug" class="portrait" /> Quisque sit amet justo. Cum sociis...</p> </div> </div> </body> </html>
By now, this sort of markup should, we hope, feel rather old hat to you. The markup is simple, yet well meaning, with proper heading elements (h1 and h2) applied to suit their position in our (admittedly nonsensical) document’s outline. Paragraphs have been marked up as such via the <p> element, with one earmarked with an id of “blurb” so that we might later style it differently than its siblings. And just to spice up the layout a bit, we’ve included a pixel illustration of one of our authors. Sorry, no door prizes are available for guessing which one it is.
Figure 8-1 shows you just how humble our beginnings are.
Figure 8-1: Our unstyled XHTML document, partying like it’s 1994
And again, as always, we can apply a rough style sheet to this document, and slap a bit of mascara on that otherwise unimpressive wall of serifs. To begin, let’s create a new style sheet called core.css, and include the rules in Listing 8-2.
Listing 8-2: The core.css Style Sheet
/* /files/includes/default.css font and color information */ body { background: #FFF; color: #444; font: 62.5%/1.6em "Lucida Grande", Verdana, Geneva, Helvetica, Arial, sans-serif; } /* END /files/includes/default.css font and color information */ /* /files/includes/default.css link rules */ a { color: #C60; } a:hover { color: #F60; text-decoration: none; } /* END /files/includes/default.css link rules */ /* headings */ h1, h2 { color: #B61; line-height: 1em; font-weight: normal; font-family: Helvetica, Arial, Geneva, Verdana, sans-serif; margin: 1em 0; padding: 0; } h1 { font-size: 2.2em; } /* END headings */ /* container */ #container { margin: 0 auto; max-width: 60em; } /* END container */ /* content */ #content h2 { font-size: 1.2em; text-transform: uppercase; } #content p { font-size: 1.1em; line-height: 1.6em; } #content img.portrait { float: right; margin: 0 0 1em 1em; } #content span.lead { text-transform: uppercase; } #content #blurb { background: #FFC; border: 1px dotted #FC6; color: #000; font-size: 1.5em; line-height: 1.4em; padding: .5em; text-align: center; } /* END content */
Pardon us while we pause for breath—that was a bit of a rush, wasn’t it? Rather than adding a few selectors at a time and discussing the visual result, we’re taking a less Socratic approach in this chapter. The focus here is less upon the techniques the style sheet contains than upon the end result gained by switching them. After all, this CSS is merely a placeholder, one that could easily be replaced by your own efforts. With that, we won’t bother to preen over this one. However, following are a few techniques worth briefly describing.
#container {margin:0 auto;}
. While some in the audience might be tut-tuting our use of adiv
with no real semantic worth, our#container
establishes a handy means of controlling the width of the content within it. Setting left- and right-hand “auto” margins (auto;
) allows us to horizontally center thediv
within its parent element—namely, thebody
of our markup.The only issue with this approach is a slight one. Versions 5.x of Internet Explorer on Windows doesn’t properly implement auto-margins, and will, therefore, not understand this rule. As a result, we need to applytext-align: center;
to thebody
element. Granted, this is an incorrect interpretation of thetext-align
property (designed to control the inline content of a block, and not the block itself), but it nonetheless puts IE5/Windows back in its place. But, by applyingbody {text-align: center;}
, IE5/Windows also centers all of the text on the page. Thankfully, once we set#container {text-align: left;}
, our work is finally finished.#container {max-width:60em;}
. Setting themax-width
property completes the effect, ensuring that the#container
element never gets larger than 60 ems. If the user reduces the size of his or her window below the width of#container
, then thediv
will shrink accordingly. In short, it’s a no-hassle way to gain a truly flexible layout.- The largest drawback to the
max-width
property is Internet Explorer’s complete lack of support for it. Neither the Macintosh nor the Windows version of that browser will understandmax-width
. As a result, we have to serve up a defined width to IE. We’ve decided uponwidth:60em;
, but you might also opt to choose a more flexible percentage width. Serving this alternate value to IE and IE alone can be done through the judicious use of a CSS hack, or (our favorite method) by placing the “incorrect” value in a separate style sheet with browser-specific hacks. Chapter 2 discusses both of these concepts in more detail. body {font:62.5%/1.6em "Lucida Grande",Verdana,Geneva,Helvetica,Arial,sans-serif;}
. The font property we’ve set on the body element is actually a shorthand property. Here, we’ve declared thefont-size
(62.5%),line-height
(1.6em) andfont-family
(Lucida Grande, Verdana, Geneva, Helvetica, Arial, and sans-serif) in one handy-dandy property/value pair. And furthermore, because these values are inherited by the body’s descendant elements, we’ve immediately applied a basic type profile to the entire document with this one rule.The 62.5 percent value forfont-size
is a technique first publicized by Web designer Richard Rutter (How to Size Text Using ems). Because the /files/includes/default.css text size for all modern browsers is 16 pixels, settingfont-size:62.5%;
on the body nets us a /files/includes/default.css type height of 10 pixels (16 @@ts 0.625 = 10). From there, sizing descendant elements with ems (a relative unit of measurement) becomes much more logical: 1em is 10px, 1.6em is 16px, .9em is 9px, and so forth. Rather than using pixels throughout our document (which IE users cannot resize), this method gives us a near-pixel-perfect replacement for our typesetting, and one that allows users to resize text to a size that suits their needs.#content img.portrait {float:right;}
. The oh-so-handsome pixel portrait is aligned flush with the rightmost edge of its containing paragraph. But rather than resorting to deprecated markup techniques such as<img src="blah.gif" align="right" />
, we’re using the powerful CSSfloat
model to achieve the same effect. We recommend that you read CSS guru Eric Meyer’s excellent article on Containing Floats.
With a better grip on some of the more clever points in our style sheet (as if that says much), we could easily paste this entire chunk of code into a <style type=“text/css”>…</style> block in the head of our document. Of course, that would muddy our markup with presentational information—and honestly, who wants to hunt down style information across a few hundred XHTML documents? That’s right, some call it “adhering to a strict separation between structure and style.” We call it “lazy.”
In either event, let’s create a second style sheet, and name it main.css; at the top of that new file, we’ll use the @import command to invoke our core.css file, like so:
@import url(/files/includes/quot-core.css-quot-.css);
We’ve effectively created a “wrapper” style sheet—a CSS file that acts as a gateway to other style sheets. With this main.css file in hand, we can now include it—and with it, core.css—in our XHTML with one simple link element, placed in the head of our document:
<link rel="stylesheet" href="main.css" type="text/css" />
And voilà! Our rather plain-looking document suddenly gets a bit of personality in Figure 8-2.
Figure 8-2: Applying some basic styles to our XHTML makes it a bit more readable.
After creating this seemingly superfluous main.css file, there might be some head-scratching in the audience. But rest assured, there are some very good reasons for putting this file in place. Although it wasn’t designed as such, the @import rule is a kind of litmus test for a browser’s support of advanced CSS techniques. Legacy browsers that have broken CSS implementations don’t understand the @import rule, and will simply disregard it. This allows us to serve up high-octane style sheet rules to modern browsers, while such antiquated browsers as versions 4 and below of Netscape and Internet Explorer will simply ignore the style sheets that they wouldn’t otherwise understand.
An added benefit to this intermediary style sheet is that we can place multiple @import rules therein. This could come in handy if we needed to break our site’s presentation into multiple files (for example, one for layout, another for color, yet another for typography). Or even better, we can use this technique to manage our various CSS hacks, as we saw in Chapter 2. As you test the CSS in Listing 8-3, you may find that the different versions of Internet Explorer (both on the Macintosh and Windows platforms) break different aspects of the layout. While we could use a battery of style sheet hacks within our core.css file to serve up alternate property values to these browsers’ broken CSS implementations, Listing 8-3 shows how we might use our wrapper style sheet to include browser-specific CSS hack files, which allow us to keep our main.css file clean and hack-free.
Listing 8-3: A Revised core.css File, with Intelligent Hack Management
@import url(/files/includes/quot-core.css-quot-.css); /* Import WinIEx-only bugs - hide from Mac IE5 */ @import url(/files/includes/quot-hacks.win.iex.css-quot-.css); /* END hide from Mac IE5 */ /* Import Win IE5x hacks */ @media tty { i{content:"";/*" "*/}} @import '/files/includes/hacks.win.ie5.css'; /*";} }/* */ /* Import Mac IE5 hacks */ /**//*/ @import url(/files/includes/quot-hacks.mac.ie5.css-quot-.css); /**/
We’ve already discussed the CSS hacks needed to get our page looking good in less-than–CSS-savvy browsers, and triaging the different hacks into browser-specific files is an excellent way to keep our core.css file clean and hack-free. If we ever decide to drop support for a certain browser, we now need to remove only a few lines from main.css—definitely a more appealing thought than scouring our primary style sheet rules line by line, looking for CSS hacks. Again, it’s equal parts “strategic” and “lazy” here at CSS Best Practices Headquarters.
Now that we’ve put our CSS and XHTML firmly in place, we can delve into the mechanics of style sheet switching.
CSS Switching
One fault of our page’s current design is that legibility wasn’t one of our guiding design goals. The contrast is a bit light, as we opted to use a near-black color for the text against the body’s white background. And the /files/includes/default.css font size of 62.5 percent of the browser’s /files/includes/default.css (or roughly 10 pixels) might be difficult to read for users suffering from visual impairments. (Even a reader with slight myopia might have to work at reading our content.) How can we improve the design to make it more legible, without sacrificing our original vision?
To begin, let’s create a separate style sheet that addresses some of these possible pitfalls. In Listing 8-4, we’ve created a new CSS file named contrast.css.
Listing 8-4: The contrast.css Style Sheet
body { background: #000; color: #DDD; } h1, h2 { color: #FFF; font-weight: bold; } #content { font-size: 1.1em; } #content h2 { font-size: 1.6em; text-transform: none; } #content #blurb { background: #222; border-color: #444; color: #FF9; } span.lead { font-weight: bold; }
Now let’s simply add a link to our new contrast.css file in the head of our markup, like so:
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="stylesheet" href="contrast.css" type="text/css" />
When we reload the document in our browser, Figure 8-3 shows us that the landscape has changed pretty drastically.
Figure 8-3: Now we’re on our way, as we’ve added a supplementary style sheet that provides heightened contrast and an increased font size.
First and foremost, it’s worth mentioning that because the two CSS files are being included in tandem, we don’t need to use contrast.css (Listing 8-4) to re-declare any of the layout or type rules established in main.css. Rather, we can simply selectively override individual rules and/or property values, and let the rules of specificity handle which rules cascade to the user.
From a purely aesthetic point, we’ve instantaneously changed the presentation of our markup—and all by including the new contrast.css file. The off-black text has been replaced with pure white, the text size has been increased very slightly (from 1em to 1.1em), and the colors on our pull quote have been changed to reflect the new palette. The completed effect feels much more nocturnal—but more important, we’ve created a style sheet that allows users to enjoy a higher level of contrast, as well as a slightly more legible type size.
But we’ve still not settled our original problem. How do we switch between the two style sheets? We grew pretty attached to that white, open design—it would be a real shame to lose it, wouldn’t it?
Oh, at least pretend it’s pretty. Please?
The Mechanics: How It’s Supposed to Work
Thus far, we’ve associated two separate CSS files with one document. Currently, they’re both being read and applied to the document with equal weight, with the rules of specificity resolving any conflicts that may arise between the two. However, to accommodate more complex scenarios than the one we currently have, the HTML and CSS specifications outline structured guidelines for how multiple style sheets interact. Web page authors are given a number of ways to prioritize the style sheets we include via the link element. Let’s examine the three different classes of style sheets (no pun intended), and see how they might apply to our switching scenario.
Persistent Style Sheets
Persistent style sheets are always enabled. Think of them as CSS that is “turned on” by /files/includes/default.css. The persistent CSS file will be applied in addition to any other style sheets that are currently active, and acts as a set of shared style rules that every other style sheet in the document can draw upon.
Each link element with a rel attribute set to “stylesheet” is a persistent style sheet—and, in fact, we’ve created two already:
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="stylesheet" href="contrast.css" type="text/css" />
As we add additional kinds of style sheets, any links that we designate as persistent will act as the baseline, sharing their rules with all other included CSS files.
Preferred Style Sheets
By adding a title to a persistent style sheet, we can designate a style sheet as preferred, like so:
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" />
Additionally, we can specify multiple “groups” of preferred style sheets by giving them the same title attribute. This allows the user to activate (or deactivate) these groups of CSS files together. Should more than one group be present, the first group will take precedence.
Much as with persistent style sheets, preferred CSS files are enabled by /files/includes/default.css. So, in the previous example, our contrast.css file (refer to Listing 8-4) would be enabled when the user first visits our page (borrowing, as it did before, from our persistent main.css file). However, preferred style sheets are disabled if the user selects an alternate style sheet.
Alternate Style Sheets
An alternate style sheet can be selected by the user as, well, alternatives to a CSS file marked as preferred by the site’s author. To designate a link as an alternate style sheet, it must be named with a title attribute, and its rel attribute set to “alternate stylesheet”. As with preferred style sheets, we can group links together by giving them identical title attributes. So, in short, this is what we’ve been looking for—a means through which we can allow users to select the design that best suits their needs. If we do, in fact, want main.css to be the /files/includes/default.css and contrast.css to be an optional, alternate CSS file, then we should update our two link elements to match:
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="alternate stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" />
Now, viewing the page in a browser that supports style sheet switching, the user can finally control the display of the page. Browsers such as Firefox or Opera include an option to select our new alternate style sheet, as shown in Figure 8-4.
Figure 8-4: By changing the rel attribute of the second link element to “alternate stylesheet” and supplying a title, we’ve implemented some basic style switching.
Once the user selects Higher Contrast from the menu, the alternate style sheet with that title—namely, contrast.css—becomes active. So, we’ve finally settled on the solution we were looking for. Our original design is the active /files/includes/default.css, but we’ve created the means through which users can select another design altogether. Using this method, we can add even more alternate CSS options. Let’s create a file named hot.css and use the rules in Listing 8-5.
Listing 8-5: The hot.css Style Sheet
body { background: #000 url(/files/includes/images/articles-strategies_for_css_switching-quot-bg-stylish.jpg") no-repeat 50% 0; color: #DDD; } h1, h2 { color: #FFF; font-weight: normal; text-align: center; text-transform: none; } #content { font-size: 1.1em; } #content h1 { font: 2.6em Zapfino, "Gill Sans", Gill, Palatino, "Times New Roman", Times, serif; margin: 200px 0 70px; } #content h2 { font: 1.6em "Gill Sans", Gill, Palatino, "Times New Roman", Times, serif; margin: 1.4em 0; text-transform: uppercase; } #content #blurb { background: #222; border-color: #444; color: #FF9; } span.lead { font-weight: bold; }
And now, by applying what we’ve learned about alternate style sheets thus far, we can easily present hot.css (refer to Listing 8-5) to our users as another user interface option:
<link rel="stylesheet" href="main.css" type="text/css" /> <link rel="alternate stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" /> <link rel="alternate stylesheet" title="Gratuitous CSS" href="hot.css" type="text/css" />
If our users have the ability to select another alternate CSS file from their browsers, then they’ll be able to see our new styles as shown in Figure 8-5. And, as before, the change is a fairly drastic one—but we’ve finally allowed the users to choose an appealing design and tailor our content to meet their needs.
Figure 8-5: With our understanding of how the cascade works, we can build even more complexity into our alternate CSS documents.
Another Solution We (Almost) Can’t Quite Use
As with some of the most promising features of CSS, adoption of alternate style sheets would be more widespread if browser support were more robust. As of this writing, the number of browsers that natively allow users to select alternate style sheets is limited to Gecko-based browsers such as Mozilla or Firefox, and the Opera browser. For example, Apple’s Safari has no way to select alternate or preferred style sheets. And, you guessed it, Internet Explorer (the browser known and loved the world over) won’t allow users to select the alternate user interfaces we build for them. If the world’s most popular browser keeps this feature out of the hands of our users, then we have a bit more work to do yet.
Furthermore, the browsers that do natively support alternate style sheet switching have only a limited switching functionality. While these browsers do allow the user to easily switch between the /files/includes/default.css CSS and any alternates provided by the author, they do not remember the user’s selection. This means that, if a reader selects an alternate style sheet and then reloads the page or leaves and returns to it, the browser will forget the earlier choice and reinstate the /files/includes/default.css style.
Obviously, neither of these scenarios will work for our users. We’re lucky that there are some additional steps we can take to bring the full benefits of CSS switching to them.
The Reality: How It Can Work Today
We’ve established that most of our audience won’t be able to use in-browser CSS switching (and identified those who don’t have much functionality available to them) so we must build an interface into our page that allows users to overcome these limitations. Now, you might realize that the two client-side technologies we’ve been studying up to this point aren’t especially well equipped to handle this. While XHTML and CSS excel at describing and styling content, respectively, neither was designed to interact with the user. Sure, we can use XHTML to build a list of links on the page as follows:
<div id="switcher"> <ul> <li id="style-/files/includes/default.css"><a href="styleswitch.html">Default style</a></li> <li id="style-contrast"><a href="styleswitch.html">Higher Contrast</a></li> <li id="style-hot"><a href="styleswitch.html">Gratuitous CSS</a></li> </ul> </div>
And we can add some CSS to core.css (refer to Listing 8-2) to style them accordingly, as shown in Figure 8-6:
/* switcher styles */ #switcher ul { text-align: right; list-style: none; } #switcher ul li { border-left: 1px solid; list-style: none; display: inline; padding: 0 0 0 1em; margin: 0 1em 0 0; } #switcher #style-/files/includes/default.css { border-left: 0; padding-left: 0; } #switcher ul a.now { color: #000; font-weight: bold; text-decoration: none; } /* END switcher styles */
Figure 8-6: We’ve added the links for our switcher to the top of our page, but all they can do at the moment is look pretty—and we’re about to change that.
However, what happens when the user clicks on those links? If your answer was something akin to “zilch,” then you win the blue ribbon. XHTML and CSS can’t really do anything when you’re talking about responding to a user’s actions. They can, in turn, affect the content and the presentation of the page, but when the user tries to click a link to change the active style sheet, that’s where we need to turn to the third tool in the standards-savvy designer’s toolkit: JavaScript.
Jumping on the JavaScript Bandwagon
To put it simply, JavaScript was created as a client-side scripting language. JavaScript (or JS, to use the parlance of lazy typists everywhere) is a language designed to add a layer of interactivity into our Web pages. When a user visits a Web page that has some JavaScript code in it, the browser reads the JS, and then follows any instructions that might be contained therein. Those instructions might tell the browser to display helpful messages to a user as he or she completes a form, or to perform basic validation on the data he or she enters there. We can even use JS to instruct the browser to perform a certain action when the user clicks a link. In short, JavaScript is the means through which we bridge the divide between our content and our users, allowing the latter to fully interact with the former.
Sounds intimidating (and more than a little stuffy), doesn’t it? Perhaps it’d be best to just dive right in.
Gathering Requirements
Before we begin a lick of coding, we should make sure that we understand exactly what it is that we’re building. Just as we discussed the benefits of requirements gathering to a client project in Chapter 1, the smallest development gig can benefit from some sort of needs analysis. With a better understanding of what we need to build and the goals it should achieve, we can code more quickly and efficiently—two qualities that will make our clients and us quite happy.
So let’s take a quick inventory of what we’re working with:
- We have three link elements in the head of our XHTML document that include screen-specific CSS files: a persistent style sheet (main.css), and two alternate style sheets (contrast.css in Listing 8-4 and the ultra-swank hot.css in Listing 8-5).
- Accordingly, at the top of our document we’ve created a list of three anchors, each corresponding to a different style sheets. Granted, these anchors are about as useful as a road map in the desert, but we’re going to change that shortly.
With this in mind, what exactly should our function do? Ideally, when a user clicks a link:
- The function should cycle through each of the link elements in the head of our XHTML, and inspect those that link to style sheets and have a title.
- If the link matches the link that the user selected, then it should be set to be the “active” CSS.
- Otherwise, the link should be set to “disabled,” which will prevent the browser from loading the style sheet.
- Once the function has finished setting the active link element, it should remember the user’s choice. The style sheet the user selected will, therefore, remain “active” as the user browses through the site, and the choice will be remembered if the user returns to our site during a later browsing session.
How, you may ask, will we do all of this? Well, the solution ultimately involves a fair amount of pixie dust and happy thoughts—but we shouldn’t get too far ahead of ourselves.
Building the Switching Function
With our goals firmly in mind, we can begin building our style sheet functions. Let’s create a new file called scripts.js, and include the following markup in the head of our XHTML document:
<script type="text/javascript" src="scripts.js"></script>
Much as we’re using the link element to include external CSS files for our site’s presentation, we can use the script element to reference an external JavaScript file. And, in that file, we can write in the first lines that will power our CSS switcher. If JavaScript syntax looks a bit intimidating, don’t worry. We’ll simply touch on some of the highlights, and get back to that “Professional CSS” malarkey as quickly as possible.
// activeCSS: Set the active stylesheet function activeCSS(title) { var i, oneLink; for (i = 0; (oneLink = document.getElementsByTagName("link")[i]); i++) { if (oneLink.getAttribute("title") && findWord("stylesheet", oneLink.getAttribute("rel"))) { oneLink.disabled = true; if (oneLink.getAttribute("title") == title) { oneLink.disabled = false; } } } } // findWord: Used to find a full word (needle) in a string (haystack) function findWord(needle, haystack) { return haystack.match(needle + "\b"); }
In this code snippet, we have two JavaScript functions, which are basically discrete chunks of functionality. The two functions we’re looking at here are activeCSS() and findWord(). Each function contains a series of instructions that are passed to the browser for processing. For example, when activeCSS is invoked, it performs the following tasks:
- It assembles a list of all link elements in our document (document.getElementsByTagName(“link”)), and proceeds to loop through them.
- For each link element found, it checks to see if there is a title available, and then evaluates the rel attribute to see if the word “stylesheet” is present. The findWord() function is used here to search the rel for a whole-word match only. This means that if someone accidentally types rel=“stylesheets” or the like into their link element, our function ignores them.
- Each link that meets the criteria in Step 2 will be disabled (oneLink.disabled = true;).
- When the function is first invoked, an argument (or variable) is passed with the title of the desired “active” style sheet. So, as the function loops through each of the link elements, it checks to see if the title of the link matches the title of the function’s argument. If so, the link element is reactivated.
Admittedly, this is a bit of a gloss of the functions’ syntax. JavaScript is a robust and rewarding language, but we’re nonetheless forced to breeze through some of its subtleties to get back on the CSS track. However, the preceding list demonstrates the high-level concepts at play in the code we’ve created, and should provide a fine starting point for those interested in further exploring JavaScript’s elegant syntax.
While these two functions enable us to switch our CSS, they simply lie dormant until they are invoked (or called) by our markup. Because we want our switcher to fire when a user selects a link from our #switcher list, the easiest place to do so is within the anchors of our style switcher list:
<div id="switcher"> <ul> <li id="style-/files/includes/default.css"><a href="styleswitch.html" onclick="activeCSS('/files/includes/default.css'); return false">Default style</a></li> <li id="style-contrast"><a href="styleswitch.html" onclick="activeCSS('Higher Contrast'); return false">Higher Contrast</a></li> <li id="style-hot"><a href="styleswitch.html" onclick="activeCSS('Gratuitous CSS'); return false">Gratuitous CSS</a></li> </ul> </div>
The onclick attribute we’ve introduced here is called an event handler. When the user performs a certain action or “event” (such as, in this case, a mouse click), the JavaScript contained in the attribute value is fired. So, in the preceding example, the onclick handler will detect when a user clicks on the anchor, and will in turn fire the activeCSS() function.
Strictly speaking, you could argue that use of these event handlers, such as onclick, onblur, onmouseover, and so on, is analogous to relying on the style attribute—that these inline attributes blur the separation of structure and behavior, and can easily increase the cost of maintenance and support. Rather than editing our XHTML to reflect any changes in the JavaScript, it would instead be possible to use more modern JS to automatically generate the event handlers our links will need, and, therefore, keep the necessary divide between our markup and our scripting. For more information, we recommend Peter-Paul Koch’s Separating behavior and structure.
However, as we look closely at the three different event handlers, we can see that each reference to activeCSS() differs slightly. Between the parentheses, we’ve included the title of the style sheet the link should activate. This is the argument we mentioned earlier and is the string of text that the activeCSS() function compares to the title of each link element.
You may have noticed that after the call to the activeCSS() function, the onclick handler contains some additional text: return false;. This plays a very small (but integral) part in our switcher because it tells the handler not to follow the URL referenced in the anchor’s href attribute. Otherwise, the user would end up deposited on styleswitch.html after clicking any of the links.
So, let’s do a bit of role-playing. Let’s just run through the steps that occur when we click a link. For argument’s sake (oh, aren’t we clever), let’s assume that our user selects the third anchor, the onclick handler that contains the activeCSS(‘Gratuitous CSS’); reference:
- The three link elements are compiled into an array, and the function proceeds to loop over each of them. Remember that only those links that contain titles and that have a rel attribute that contains the word “stylesheet” will be examined. This leaves us with the links for contrast.css (refer to Listing 8-4) and hot.css (refer to Listing 8-5).
- The first link element has a title of “Higher Contrast.” The function disables the link element. Because its title doesn’t match our function’s argument (“Gratuitous CSS”), it stays disabled.
- The second link element has a title of “Gratuitous CSS.” The function disables the link element. Because the title does match our function’s argument, the link is immediately reactivated.
And voilà! As you can see in Figure 8-7, we’ve completed the effect. Clicking each anchor activates the alternate style sheet whose title matches the one referenced in the activeCSS() function call.
Figure 8-7: With our JavaScript-enabled style switcher in place, our users can now select a look that best suits their needs.
However, even though we’ve successfully built a function to switch between the different CSS files, we’re only halfway there. If the user refreshes the page or leaves the current one after selecting a new alternate style sheet, the choice is forgotten, and the /files/includes/default.css style sheet is restored. So, obviously, we’ve a bit more work to do. Let’s see if we can’t put a little memory into our JavaScript functions.
Baking a JavaScript Cookie
As you’ve seen with our not-quite-finished CSS switcher, our browsers don’t seem to remember anything about a page once we’ve left or refreshed it. This is by design. The HTTP standard (which is the protocol over which the Web’s pages are transferred from a server to your desktop) was designed to be “stateless.” This means that each time you visit a page, the Web server considers it to be your first time, every time. Thankfully, we have a way to fill this memory gap. It’s called a cookie, and it’s less fattening than its baked namesake.
A cookie is a small text file that is sent by a Web server to a user’s browser, and contains small bits of important information about that user’s browsing session. Cookies may contain user preferences, registration information, or the items placed in an online shopping cart, and so on. Once it receives a cookie, the browser saves the information on the user’s computer, and sends it back to the Web server whenever the user returns to that Web site.
We’re not just flapping our gums here. We’re mentioning cookies because we can use JavaScript to set and read them. So, armed with this knowledge, we can finally see our way to the finish line. By adding a few more JavaScript functions to those we’ve already written, we can build an improved style sheet switcher, and one that will respect our user’s preferences across multiple pages, or visits to, our site.
From this, we need two tools in our cookie-baking toolkit (we can mix metaphors with the best of them). We need to be able to set a cookie containing our user’s style preference, and to then later read the cookie. So, let’s add a new cleverly named function to our scripts.js file, setCookie():
// Set the cookie function setCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = ";expires="+date.toGMTString(); } else { expires = ""; } document.cookie = name+"="+value+expires+";"; }
And now, in our original activeCSS() function, we can add a single line to store our user’s preferences in a cookie on the user’s computer:
// Set the active stylesheet function activeCSS(title) { var i, oneLink; for (i = 0; (oneLink = document.getElementsByTagName("link")[i]); i++) { if (oneLink.getAttribute("title") && findWord("stylesheet", oneLink.getAttribute("rel"))) { oneLink.disabled = true; if (oneLink.getAttribute("title") == title) { oneLink.disabled = false; } } } setCookie("mystyle", title, 365); }
With this one line, half of our work is finished! The setCookie() function accepts three arguments: a name for the cookie (so that we might later reference it), the value to be stored in the cookie, and the number of days until the cookie expires. So, in the previous code snippet, we’ve created a cookie named “mystyle”, the value of which is set to the value of the title argument of activeCSS(). This means that if a user selects a link that specifies activeCSS(‘Higher Contrast’) in its onclick handler (that is, it invokes activeCSS with a title argument of Higher Contrast), then our “mystyle” cookie will, therefore, have a value of Higher Contrast.
In our setCookie() function, specifying the number of days until cookie expiration is optional. The latter argument is optional. In the preceding example, we’ve arbitrarily decided to set the “mystyle” cookie to expire in 365 days, or one calendar year. Because the argument is optional, we could leave it out entirely. However, omitting it will cause the setCookie() function to create a “mystyle” cookie that expires at the end of the user’s session—causing the user’s preference to be lost as soon as he or she closes the browser.
So, with this lone call to setCookie(), we’ve managed to store the user’s selection from our list of style sheet anchors. But now that we’ve stored it, how do we read the cookie and honor the preference? Well, once we place the following lines in our scripts.js file, it would seem that the answer is, “simply enough”:
window.onload = initCSS; // initCSS: If there's a "mystyle" cookie, set the active stylesheet when the page loads function initCSS() { var style = readCookie("mystyle"); if (style) { activeCSS(style); } } // Read the cookie function readCookie(name) { var needle = name + "="; var cookieArray = document.cookie.split(';'); for(var i=0;i < cookieArray.length;i++) { var pair = cookieArray[i]; while (pair.charAt(0)==' ') { pair = pair.substring(1, pair.length); } if (pair.indexOf(needle) == 0) { return pair.substring(needle.length, pair.length); } } return null; }
With these last lines of JavaScript in place, we’re finally finished. Our new function, initCSS(), has two simple tasks. First, it checks to see if there is a “mystyle” cookie on the user’s machine (var style = readCookie(“mystyle”);). If one is present (if (style)), then the activeCSS() function is invoked with the value of the user’s cookie as its argument.
But it’s the first line of this code snippet that does the heavy lifting, even though it looks rather innocuous. window.onload = initCSS; fires our initCSS() function when the document finishes loading in the user’s browser. Now, as the user moves between the pages of our site (hypothetical though they may be), or when the user returns during a later session, we can immediately poll for the presence of a “mystyle” cookie as each of our pages comes up. As the user makes selections from our style switcher, our pages will honor them, allowing the user to tailor not only individual pages, but an entire site to his or her browsing needs.
Listing 8-6 shows the complete JavaScript file.
Listing 8-6: The Complete scripts.js File That Powers Our JavaScript-Enabled CSS Switcher
/* Onload */ window.onload = initCSS; // initCSS: If there's a "mystyle" cookie, set the active stylesheet when the page loads function initCSS() { var style = readCookie("mystyle"); if (style) { activeCSS(style); } } /* Switcher functions */ // activeCSS: Set the active stylesheet function activeCSS(title) { var i, oneLink; for (i = 0; (oneLink = document.getElementsByTagName("link")[i]); i++) { if (oneLink.getAttribute("title") && findWord("stylesheet", oneLink.getAttribute("rel"))) { oneLink.disabled = true; if (oneLink.getAttribute("title") == title) { oneLink.disabled = false; } } } setCookie("mystyle", title, 365); } // findWord: Used to find a full word (needle) in a string (haystack) function findWord(needle, haystack) { var init = needle + "\b"; return haystack.match(needle + "\b"); } /* Cookie functions */ // Set the cookie function setCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = ";expires="+date.toGMTString(); } else { expires = ""; } document.cookie = name+"="+value+expires+";"; } // Read the cookie function readCookie(name) { var needle = name + "="; var cookieArray = document.cookie.split(';'); for(var i=0;i < cookieArray.length;i++) { var pair = cookieArray[i]; while (pair.charAt(0)==' ') { pair = pair.substring(1, pair.length); } if (pair.indexOf(needle) == 0) { return pair.substring(needle.length, pair.length); } } return null; }
Down with PHP
We hope we’re not becoming incredibly predictable by now. But since we’ve completed our JavaScript solution, we feel compelled to point out some potential issues with it. Actually, we’ll limit ourselves to one drawback because it’s the largest: How do we know that the user has JavaScript on his or her machine? Okay, we can almost hear the snorts of derision from here—after all, JavaScript is the new black, right? What browser doesn’t have this ultra-cool language available to it?
As it turns out, quite a few. Internet statistics repositories such as TheCounter.com (www.thecounter.com/) suggest that anywhere from 9 percent to 11 percent of all Web users are browsing without JavaScript. It’s true; it does sound like betting odds—1 in 10 isn’t so bad, right? Well, once we remember that we’re talking about millions of people in that group of “9 percent to 11 percent,” then the demographic starts to look a little different. And, regardless of exactly how many people browse without JavaScript, why should we exclude any from accessing our site? Especially since (as you’ll soon see) it’s incredibly easy to replicate the same functionality with server-side programming.
Rather than relying on client-side code that may or may not be available to our users, we can instead build a script that resides on our Web server to handle the style switching. Because we’ll be working in a server-side environment, we can stop worrying about whether or not JavaScript is active on our users’ computers. As long as our users can accept cookies, our server-side script will be able to handle the style sheet switching logic with ease.
Of course, there are nearly as many server-side programming languages as there are authors on this book. For our purposes, we’ll use PHP (www.php.net/). It’s a wildly popular, open source (read: “free”) programming language, and is available on a staggering number of today’s Web servers. Because of its popularity, its speed, and its robust feature set, it makes a fine choice for this chapter’s experiments.
And besides, it’s free. Did we mention that? Free. We rather like that.
Of course, PHP isn’t a magic bullet—if it’s not installed on our Web server, we can’t take advantage of it. So while we’re no longer reliant on our readers’ browser configuration , we’ve replaced it with a technical requirement on our server. Contact your server’s administrator to see if it’s installed on yours—otherwise, if you want some help getting PHP installed on your machine, there are plenty of resources available.
The official PHP documentation (www.php.net/docs.php) is possibly the best place to start—although in all honesty, we find their installation instructions (while very clearly written) a bit intimidating for those of us somewhat lacking in the 133t-ness category. If you find yourself a bit lost among the configuration instructions, we recommend resorting to your favorite search engine. A search for “install php windows” or “install php mac” will yield hundreds of (we hope) easy-to-read results, yet another testament to PHP’s popularity as a powerful, robust programming language.
Additionally, Mac OS X users can avail themselves of easy-to-install packages. We personally recommend Server Logistics’ feature-rich PHP installer (www.serverlogistics.com/php4.php), but other comparable packages are available. The drawback to such packages is that it limits the amount of control we can exercise over PHP’s configuration. If an “as-is” installation isn’t appealing to you, then the official documentation is the best resource out there.
Creating the Script
Once we have PHP up and running on our server, we can get to work. To begin, let’s modify our XHMTL—specifically, the unordered list that contains the different style sheet options. Whereas we previously used onclick handlers to do the dirty work, the landscape has changed somewhat:
<ul> <li id="style-/files/includes/default.css"><a href="switch.php?style=">Default style</a></li> <li id="style-contrast"><a href="switch.php?style=contrast">Higher Contrast</a></li> <li id="style-hot"><a href="switch.php?style=hot">Gratuitous CSS</a></li> </ul>
Now, all of our links are currently pointing to a file named switch.php—but what’s all of that ?style= stuff in our link? The text that follows the question mark in the href is known as a query string, and allows us to pass parameters to our CSS switcher script. Query string parameters always come in name/value pairs, like so:
file.name?name=value switch.php?style=contrast
If we want to pass multiple name/value pairs to our script, then we concatenate them with ampersands (&):
Switch.php?style=contrast&font=serif&css=cool
In HTML, ampersands have a special meaning. They are used to signal the start of character entities, codes that represent a special character in HTML. For example, © is the entity for @@cw, ™ will display ™ in our browser, and so forth. However, when we want a literal ampersand to appear, as we do in our query string, we need to use &, which is the proper entity reference. Otherwise, our HTML will be invalid, and our query string may break—and we don’t know about our readers, but those two options aren’t appealing to us.
You’ll see shortly how these parameters play a part in our style switcher, but for now let’s create this switch.php file, and paste in the following code:
<?php $domain = "my-site-here.com"; if (stristr($_SERVER['HTTP_REFERER'], $domain)) { $bounce_url = $_SERVER['HTTP_REFERER']; } else { $bounce_url = "http://$domain/"; } setcookie('mystyle', $_GET['style'], time() + 31536000); header("Location: $bounce_url"); ?>
And that’s it. No, really—this won’t be evolving into 80 lines of code over the next 50 pages, we promise. In JavaScript, we had to write custom functions to handle some of the basic tasks we needed our style switcher to tackle. But many of these common tasks (such as reading and writing cookies) are already a part of the language. By using these built-in functions, we can cut down drastically on code bloat, and get our style sheet switcher out the door as quickly as possible.
The meat of this code is the antepenultimate line of code:
setcookie('mystyle', $_GET['style'], time() + 31536000);
Whereas we had to create a custom function to set a cookie in JavaScript, it seems that PHP has done the hard work for us. Its setcookie() function is readily available to us, and snaps in nicely to our script. But the one overlap with our JavaScript setCookie() function is that we’re also passing three arguments to the function, the former two being the most critical. The first argument is, as before, simply the name for our cookie, and we’re sticking with “mystyle”.
The second argument ($_GET[‘style’]) defines what’s actually stored as the value of our “mystyle” cookie. The $_GET variable is actually a named list, or associative array, of all the parameters passed to the page in a query string. Let’s assume that our switch.php page is called with the following URL:
http://my-site-here.com/switch.php?style=hot&css=cool
Given that query string, the value of $_GET[‘style’] is what follows the equal sign in the style=hot name/value pair, or “hot”; similarly, $_GET[‘css’] would return a value of cool. As a result, our setcookie() function will build a “mystyle” cookie with a value of hot, which is exactly what we (and our users) want.
The time() + 31536000 may look like we’d need a decoder ring to make sense of it, but it’s not quite as intimidating as it might seem. The time() function simply returns the current time, measured in seconds from midnight on January 1, 1970—also called “the Unix Epoch,” a common point of reference for functions dealing with time-based tasks. Once we have the current time, we’re adding a year’s worth of seconds to it (60 seconds * 60 minutes * 24 hours * 365 days = 31536000). So, with all this, we’re essentially getting the time that is exactly one year later than when the cookie was set, and using that to determine when the cookie expires.
Once the cookie’s been set, we simply redirect the user back to $bounce_url, which was set at the beginning of the file. We’ve included some extra processing at the top of switch.php, examining the user’s referrer (the URL of the page he or she was visiting before being sent to switch.php). If the user was referred from a page on our $domain (if (stristr($_SERVER[‘HTTP_REFERER’], $domain))), then we’ll simply redirect the user to it. However, if another site decided to link directly to our style switcher, we’ll set $bounce_url to our homepage ($bounce_url = $_SERVER[‘HTTP_REFERER’];).
So, we’ve successfully set the cookie in record time and redirected the user back to our site. What happens next? We need to set up some sort of logic for handling the cookie we’ve just baked. Let’s dive right in and see what we can uncover.
Eating the Cookie
This second step requires inserting some PHP code directly into our XHTML—nothing onerous, but we first need to convert our markup document into one that our PHP server can read. To do so, we simply rename the file, and change its .html extension to .php—if our system administrator has done his or her job properly, then this should be all that’s required to ready our XHTML for a little PHP-fu.
Once we’ve done so, we can insert the following code in the head of our document:
<link rel="stylesheet" href="main.css" type="text/css" /> <?php if ($_COOKIE['mystyle']) { ?> <link rel="stylesheet" href="<?= $_COOKIE['mystyle']; ?>.css" type="text/css" media="screen" /> <?php } ?> <link rel="alternate stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" /> <link rel="alternate stylesheet" title="Gratuitous CSS" href="hot.css" type="text/css" />
When our markup document is loaded in the browser, the snippet of PHP is inserted into the head. If no “mystyle” cookie has been set (or the value is just an empty string), then none of the code wrapped in the if { … } statement gets run. However, if our cookie is present, then a new link element is printed into our markup. Let’s expand on this.
According to the query strings we put in place in our #switcher unordered list, the two possible values for our “mystyle” cookie are either hot or contrast. As a result, if you click a link with an href of switch.php?style=hot, then the resulting link element will be:
<link rel="stylesheet" href="hot.css" type="text/css" />
And with that, we’ve successfully completed our PHP style sheet switcher. Building on the goals and concepts we outlined for our JavaScript switcher, we’ve now implemented a solution that allows our users to select a design at their leisure, with a much lower technical barrier for entry.
CSS Beyond the Browser
So, we’ve established that our document is looking quite fetching when viewed in a modern desktop browser. We’ve also explored a few different ways to allow users to change our sites’ presentation layer. What happens when we take that document outside of the browser context? What happens when we try to print one of our hyper-stylized designs?
Well, we’ve selected the Gratuitous CSS skin, quite a bit—let’s look at Figure 8-8.
Figure 8-8: Here’s the printed version of our page (or a preview thereof, anyway). We can definitely do better.
As you can see, a little too much of our design is showing through when it’s printed, but not enough to be particularly effective. Gone are the page’s background color and graphic, which printers won’t render by /files/includes/default.css. Additionally, the rules that govern the page’s layout are still fully intact. Our content appears to have been pushed down the page for no good reason. The white space at the top doesn’t serve any purpose, and instead clips the amount of text visible on the page. Furthermore, the small sans-serif face we’ve settled on for the body copy might be fine on-screen (where the user can increase the size of the type as he or she sees fit), but it’s less than ideal for paper. Serif faces are generally used to improve legibility when reading a page offline, and something a few points taller than our current type size might not hurt. In short, unless you’re Emily Dickinson, this printed version isn’t exactly appealing.
So, ultimately, we should create a separate design for the printed version of our page—one that emphasizes legibility over style, yet without sacrificing aesthetics altogether. Historically, this would require no small amount of overhead on our part. The days of separate, “print-only” versions of pages still loom largely in our memory. Keeping these “design-light” pages in sync with their “design-full” counterparts was an incredibly time- and resource-consuming affair that often required complicated scripts, grueling hours of manual editing, and occasionally more than a little late-night swearing at the computer monitor (not that the pain is still fresh in our minds or anything).
Media Types: Let the Healing Begin
The authors of the CSS specification anticipated this problem. They introduced the notion of media types, a means of classifying style sheets to deliver different designs to different devices such as printers, computer monitors, screen readers, handheld Internet-ready devices, and the like. Simply by earmarking our three link elements as containing “screen”-specific designs that should be delivered only to full graphic browsers (such as IE or Firefox), we can avoid some of the unpleasantness we saw earlier. To do so, we simply specify a value of “screen” in the link’s media attribute:
<link rel="stylesheet" href="main.css" type="text/css" media="screen" /> <link rel="alternate stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" media="screen" /> <link rel="alternate stylesheet" title="Gratuitous CSS" href="hot.css" type="text/css" media="screen" />
Now, when we preview our document in its printed view, Figure 8-9 shows us that things look quite a bit different.
Figure 8-9: Once we add media=”screen” to our link element, we’ve . . . well, we’ve messed up our printed document royally. Thankfully, we’re only laying the foundation.
It might not look like it, but this is, in fact, progress. By adding the media=“screen” attribute to our links, we’ve wedded our designs to one context—the browser—and divorced them from all others. So, when viewing our document in a different media type (such as a printer), we’re shown the raw, unstyled content.
We can also specify multiple media types for any given link element. For example, the Opera browser (www.opera.com/) respects the “projection” media type when browsing in its full-screen viewing mode. As a result, it disregards any CSS we reserve exclusively for the “screen” media. If we want to reuse our screen-specific style sheet in a projection environment, we can simply append it to the media attribute with a comma: <link rel=“stylesheet” href=“main.css” type=“text/css” media=“screen, projection” />.
Of course, while we can deny styles to non-browser devices, we can also deliver styles exclusively to them as well. After all, there’s no reason to suffer through an unstyled printout when our on-screen design is so robust. So, with that, let’s create a style sheet called print.css, as shown in Listing 8-7.
Listing 8-7: Our print.css Style Sheet
body { background: #FFF; color: #000; font: 12pt/1.4em Georgia, Garamond, "Times New Roman", Times, serif; } h1, h2 { font-weight: normal; margin: 1em 0; padding: 0; text-transform: small-caps; } img.portrait, #switcher { display: none; } #blurb { background: #CCC; border: 1px solid #999; float: right; font: 16pt/1.5em Helvetica, Arial, Geneva, Verdana, sans-serif; margin: 0 0 1em 1em; padding: 1em; text-align: right; text-transform: small-caps; width: 10em; }
Expecting something a bit more complex? As much as we enjoy throwing curveballs at our audience, there’s no reason to do so here. When creating a print-specific style sheet, we can use the same syntax and tactics we’ve discussed throughout the book. Whether applied in the browser or on a piece of paper, it’s still CSS—no curveballs required. Granted, there are a few things to consider when designing for print:
- Perhaps the most striking thing about our print-specific style rules is that we’re using points to control the size of our type. While points are an absolute measure of font size, we opted to use them to show the only acceptable context for their use: print styles. When designing for the screen, we’ve avoided points like the plague because of browsers’ inconsistent rendering of point sizes. For print, however, points are ideal. Of course, the other relative sizing tactics we’ve used in the past (that is, relying on ems or percentages to control the type relative to the user’s settings) are perfectly acceptable, and will, in fact, be kinder to our users’ browser preferences.
- We’ve decided that certain aspects of our markup don’t need to be displayed in the printout. Perhaps we should spare users from printing out the photo of our ugly mug (and besides, we’d prefer it wasn’t tacked up on office cube walls to be used as a dartboard). And of course, the links for our in-browser style sheet switcher are wholly pointless. With our print-specific style sheet, it’s a simple matter of specifying img.portrait, #switcher { display: none; }. Through the magic of media types, these two elements will still be available on-screen, but removed from the printed version.
After creating our print-specific style sheet, let’s include it in the head of our document. As always, we’ll use a link to do so, but we’ll take extra care to specify the correct media type: namely, “print”:
<link rel="stylesheet" href="main.css" type="text/css" media="screen" /> <link rel="stylesheet" href="print.css" type="text/css" media="print" /> <link rel="alternate stylesheet" title="Higher Contrast" href="contrast.css" type="text/css" media="screen" /> <link rel="alternate stylesheet" title="Gratuitous CSS" href="hot.css" type="text/css" media="screen" />
When we try printing again, the results should be a bit more pleasing to the eye. This time around, the screen-specific style sheets will be ignored, and our print.css will be allowed to control the presentation. As you can see from Figure 8-10, our assumptions seem to be pretty much spot-on.
Figure 8-10: And here we are, with our print-specific style sheet in place
Our minuscule, sans-serif typeface has been replaced with a much more attractive serif face. Of course, we’re not ones to settle for the Model-T of fonts so we’ve opted to use the much more attractive Garamond or Georgia for our page’s print version. And, whereas we previously styled our #blurb paragraph as a full-column block on its own row, we use the float model to pull it out of the document flow, and give our pull quote much more of an “in-content” feel.
All of this has happened independently of the progress we’ve made with our on-screen designs. Essentially, our use of media types has allowed us to create two separate and distinct “views” of our page: the on-screen (aesthetically rich) version of our on-screen design and the off-line (content-over-panâche) printed view. One markup document is displayed on multiple devices. Style sheets allow us to realize the promise of device independence, all the while keeping us from those late-night sessions of yelling at our computer monitors.
The Problem with Choice
But now that we’ve implemented our media-specific designs, we are in some respects back at square one. Now that we’ve allowed our users the ability to choose an on-screen design that works most effectively for them, we’ve imposed a print-specific style sheet on them, with no option to change it. Do our users have to sacrifice choice in non-screen media?
We love asking leading questions. In short, the answer is, “No.” We could go back to our JavaScript- and PHP-enabled style switchers and add in cases for print-specific styles. Of course, given the number of different media possibilities, our scripts (and the UI we present to our users) could become prohibitively large and difficult to maintain. What we need, then, is an elegant, scalable solution that allows us to easily and quickly manage our alternate styles for multiple media types—and all without sacrificing usability.
Stuff and Nonsense: Building a Better Switcher
We’re lucky that certain innovative folks are already thinking along these lines. Enter Stuff and Nonsense, a design studio (www.malarkey.co.uk/) based in Wales, UK. (Figure 8-11 shows its home page.) A quick browse through the studio’s portfolio (www.malarkey.co.uk/Our_work_and_our_clients.aspx) leads to two separate realizations: first, that the studio has done eye-catching, beautiful work for such globally recognized brands as Disney and the World Wildlife Fund; second, the design of each of their portfolio sites is driven by cascading style sheets, and built upon a foundation of valid XHTML.
Figure 8-11: The home page of Stuff and Nonsense (www.malarkey.co.uk/), a well-respected design boutique based in the UK.
So, obviously, Web standards are a passion over at Stuff and Nonsense. But as you browse through their site, an obvious respect for their users’ needs runs equally deep. Featured prominently on each page is a link inviting users to “Customise this site” (www.malarkey.co.uk/Customise_this_site.aspx), as shown in Figure 8-12. On the resulting page, users are able to select various style options for not only their in-browser experience, but different options for printing as well. Whether a user prefers reading printed documents in either a small sans-serif typeface or a larger serif, Stuff and Nonsense has given him or her the ability to decide. And furthermore, the user’s preferences are stored in a cookie, so that the preferences persist throughout the time spent visiting the site. The user can browse to or print any page on the site, and will be presented with the design that best meets his or her needs throughout the stay.
Figure 8-12: The “Invasion of the Body Switchers”–style switcher, seen here at the Stuff and Nonsense site, allows users to select style options for screen, print, or any other media type through one simple interface.
This is the oddly named yet feature rich “Invasion of the Body Switchers” (IOTBS) style switcher, its name derived from how its switching functionality is powered by switching the class attribute on the body element. It’s quite possibly the perfect style switcher, and the authors of IOTBS have made it freely available for download. Remarkably easy to install and configure, IOTBS affords maximum convenience to site owners and users alike. It even generates its entire interface via JavaScript, ensuring that users unable to take advantage of CSS switching won’t be presented with non-functional markup.
With a tool like IOTBS in our arsenal, we can avail ourselves more of the true power of media-specific style sheets. Its easy-to-install interface will have our users thanking us, as we’ve managed to democratize our design. They can now sand down the rough edges that don’t meet their needs, and tailor our site into something truly usable.
Meet the Designer: Andy Clarke
With our brief introduction to IOTBS behind us, let’s meet one of the minds behind it. The creative director of Stuff and Nonsense, Andy Clarke is a rare breed of Web professional. A multi-talented designer, developer, and writer, Andy has been the creative director of Stuff and Nonsense since founding it in 1998. As one-half of the team that brought the Web such an ingenious approach to style switching, Andy was gracious enough to answer a few brief questions about accessibility, high-caliber design, and how the two aren’t mutually exclusive.
Q: Andy, it’s great to have you for this little chat. We’ve just had a browse through your personal Web site (www.stuffandnonsense.co.uk/), and can tell that, aside from some sort of odd scooter fixation, you’re quite passionate about designing for the Web. Your professional biography tells us that you have a background in advertising. How is it, then, that you moved into Web design?
A: Well, it’s a long story, and I won’t bore you with all of it. When I left college (having studied for a degree in Fine Art), I worked in various jobs, but was always involved in the arts and always with technology. I was one of the first people in the UK to work with professional digital cameras and before that, with electronic retouching, in the dark days before Photoshop! My background in art always let me keep a creative eye on the job, and when I got the chance to move to a creative advertising agency in London, I jumped at the chance.
This was a time when the Web was beginning to “get commercial,” and I saw very early that the Web done well is just like advertising: communicating messages and getting an audience to identify with a client. Then, in 1998, I moved away from London, and before long, people began asking, “Can you do . . .?” Seven years later, I’m very lucky in that they haven’t stopped asking.
Q: The client list of your studio, Stuff and Nonsense, features an impressive array of brand names, including (but not limited to) the Disney Store UK and World Wildlife Federation UK. Even more impressive is the fact that all of your portfolio work is both crisply designed and built with XHTML/CSS. Why Web standards?
A: Why not? I don’t see either Web standards or (for that matter) accessibility as issues. I believe that they are simply part of doing the job “right.” One of the things that I have learned in working with clients at Stuff and Nonsense is that they rarely care “how” a job is done. What matters to them is successfully connecting with their target audience.
You mentioned Disney Store UK and I think that it is fair to say that like most clients, they did not ask for a standards-compliant site. But they were looking for reductions in download times, an altogether faster shopping experience, and easier ways for them to update their site. Implementing the design with Web Standards technologies fit the bill and achieved their goals perfectly.
The Disney Store UK site was developed using the Karova Store platform (www.karova.com/), which not only separates the presentation tier from the rest of site, but has an XML architecture rather than a database backend. XML is transformed into XHTML through XSLT, the end result being a site that is extremely flexible and will allow Disney Store UK new opportunities to deliver their content in the future, including through RSS feeds. At the end of the day, what matters to most clients is not the “tools,” but the solutions offered to them. Web standards offers more solutions and that is why Stuff and Nonsense develops only with standards.
Q: So, tell us a bit about this clever little thing you cooked up. Something about a style sheet switcher, we understand?
A: You’re referring to Invasion of the Body Switchers (IOTBS), the style sheet switcher that I wrote about on A List Apart magazine (www.alistapart.com/articles/bodyswitchers/)? Well, I can’t take credit for the really clever stuff. The technical genius behind IOTBS was my good friend James Edwards (www.brothercake.com) who took my concept and made it work.
One of the important aspects of Web standards is the ability for designers to work on presentations through CSS without changing the underlying markup (HTML or XHTML) of a Web page. Nowhere is this demonstrated better than on Dave Shea’s CSS Zen Garden (www.csszengarden.com/), where we see different designs of the same page made possible through using CSS style sheets.
“Switching” style sheets can be necessary for all sorts of reasons. Perhaps a client would like to offer visitors the ability to switch between a fixed-width or a “liquid” layout that fills the window—designer and author Dan Cederholm offers just such a choice on his site, SimpleBits (www.simplebits.com/). Alternatively, you may wish to offer visitors with low vision an “accessible” design. The possibilities are endless and sometimes the aims and results are serious, sometimes just geeky gimmicks.
Server-side and JavaScript style sheet switchers have been around for years. But what makes Invasion of the Body Switchers different is the ability to independently switch screen, printer, and other media styles. All with only one CSS and one JavaScript file. I’m very proud of IOTBS and I hope that it will help convince more designers that working with standards can expand their creative options.
Q: We see that it’s used heavily on the site of Stuff and Nonsense (www.malarkey.co.uk/). Have you used it on any professional projects? Is this something that’s important to clients?
A: What is becoming increasingly important to our clients is offering visitors choices. Style sheet switchers such Invasion of the Body Switchers can be used to offer separate design themes to different visitor groups. But by using CSS “display properties,” we can also hide and reveal content.
This has been put to great effect in several recent projects that target young people. By using the possibilities opened up by CSS and IOTBS, we no longer have to code three or more versions of an XHTML document or even an entire Web site. This reduces development times, makes our business more efficient, and ultimately saves the client money. Everyone is happy.
Q: Some designers might find it unsettling to allow users to, well, essentially revise their sites’ design. What would you say to them? Why should we let our users have greater control over the design of our pages?
A: As designers or developers of Web sites, we need to remember who we are working for. Of course it is our client who puts our food on the table, but our work is ultimately judged by site visitors. The happier they are, the happier our clients will be and the better the chance that they will come back.
The Web is unlike any other media. In television the picture stays pretty much the same no matter what size screen you are viewing on. CRT, LCD, or Plasma, 17-inch portable or 52-inch widescreen, things stay pretty much the same. On the Web, we do not simply “sit back and watch.” We have more control over how the content is delivered and Web designers must remember that visitors’ opinions matter more than their own.
Q: After poking around a bit, it seems that there have been a number of style switchers published online. Some of them rely on client-side JavaScript (as yours does), whereas others rely on some back-end coding. Is there a clear benefit to either approach?
A: Now you’re getting all technical on me! I’m only a humble designer! Many different solutions are available to implement style sheet switching; some of them are “server side” (relying on back-end languages such as PHP) and others like Invasion of the Body Switchers are “client-side,” using languages such as JavaScript. Which solution a developer chooses depends on the environment in which the site is running and the specific needs of the client.
It’s only a personal preference, but as style sheet switching is a “client function,” I prefer to use client-side solutions. That said, I can give you the exclusive news that there will be a server-side version of Invasion of the Body Switchers coming very soon.
So, I suppose that begs the question: What is it that makes your client-side switcher stand apart from the crowd?
Invasion of the Body Switchers takes a new approach to style sheet switching. Our approach does require abandoning conventional “stylesheet” and “alternate stylesheet” semantics, but this doesn’t trouble me, because:
- Many browsers do not implement native style sheet switching.
- Those that do do not apply any persistence to a selected alternate style sheet.
Other solutions rely on multiple style sheets, using <link /> elements and “stylesheet / alternate stylesheet” semantics. This adds extra server calls, but more important, it does not allow for different media styles to be selected independently of each other.
Invasion of the Body Switchers lets us target different media types independently, and gives site visitors a simple interface from which to select their preferences, all saved into a cookie until they change their mind.
IOTBS works by adding one or more unique class names to the page’s <body> tag. Styles are then defined using descendant selectors. The end result gives users much greater control over the output of your Web pages.
Q: Interesting, so what are these “media types” you speak of? Why should the CSS-savvy Web designer care about them?
A: It’s sometimes hard for designers who come to the Web from other media to understand that that not only is their work not viewed “pixel perfect” by everyone, but that people access Web content through different media. Sometimes that media is our good friend the computer monitor; sometimes it is an Internet kiosk at the airport; sometimes a handheld computer, a projected image, or even a mobile phone. Some people find it more difficult to read from a screen and like to print out pages.
In the past, preparing for all these different media types would have been cost-prohibitive, if not impossible, as it required making different versions for different viewing devices. But, with the advent of technologies that support common standards, we can create content that can be written once only, and then styled for different media output, all through the magic of CSS.
Q: Stepping back a bit, we’d be interested to hear a bit more about your design process. How do you usually work?
A: Our first job is to understand what the client is trying to communicate to his or her audience. We also get a feel for the “personality” of the company and look at their brand values (even if they haven’t done so themselves) so that we can match the tone of the design to the personality and brand values. Effective design for the Web is about effective communication between a client and their audience. That is why we focus on what and how to communicate, before we think about technical or creative issues.
We start by developing paper prototype designs, from sketches to layouts made in either Photoshop or Macromedia Fireworks. These layouts begin as simple wireframes and from them we build markup guides, often on paper, which our developers use as their XHTML structure.
Some time ago, I developed a set of naming conventions for our site development, specific names for <div>s and classes that relate to content rather than presentation (#branding rather than #header, and so on). We stick tightly to these conventions so that the entire team understands what a particular CSS rule relates to. We also have conventions for the naming of images and this also speeds development.
Our graphic layouts then develop into production art for our design team and it is rare that our final Web pages do not match the graphic layout exactly. We also get approval from the client at each stage and always work within our internal convention framework to ensure that development is as efficient as possible.
Q: And what about inspiration? When you’re staring down a tight client deadline, from where do you get your ideas?
A: I’m a real pop culture junkie. I love trashy pulp detective novels such as Mickey Spillane’s Mike Hammer. I love comics even more and works by comic artists such as “Sin City’s” Frank Miller and “Concrete” creator Paul Chadwick are a few of my passions.
You might find it unusual to hear that I am also passionate about political art from Soviet-era Russia, China, and Cuba. I find the cult of personality fascinating and across recent history there have been many terrific examples where political art in the form of posters or statues becomes almost “high” art. The most recent examples I have seen have come from pre-invasion Iraq.
I suppose that if I think about it, what these examples have in common is that they are both designed to engage an audience, drawing them into a different world. Again, it’s about communicating messages . . . and so we get back on to the subject of the Web.
Q: Are there any CSS issues that you face more regularly than others? How do you work through them?
A: CSS issues are becoming rarer for me and when one does raise its ugly head, there is usually a solution to be found by doing a quick bit of Googling. Many people with far bigger brains than mine—Brothercake, Dave Shea, Doug Bowman, and John Gallant immediately spring to mind—have found solutions to browser bugs and behaviors I would never have dreamt existed. Of course, there are times when I curse one browser or another and yell “This would be soooo much easier with tables!” But those outbursts are getting rarer.
There are now ways to fix or work around almost every CSS issue and when one does appear unexpectedly, it is important to take a logical approach, as sometimes one element in combination with another will trigger a problem.
Validation is extremely important and ensuring that my code validates is always my first move before I even open a CSS file. If my code and CSS both validate and the problem still appears, I deconstruct the page, removing elements in turn so that I can see which element is straining my sanity.
Many browser bugs are now so well-known that entire sites such as John Gallant’s Position Is Everything (www.positioniseverything.net/) are dedicated to them. If an answer can’t be found on PIE or on many other sites, I recommend asking a question of the many experts who contribute to Eric Meyer’s excellent CSS-D mailing list. Ask nicely and you’re likely to find a helpful soul with a solution.
Q: What exactly do you look for in a “successful” site design? Are there any design principles you hold especially dear?
A: I suppose that I’m not the best person to judge whether or not a design is successful, but I do listen to feedback from clients and their customers. What matters to me is that the project has made good business for the client who pays my wages. That way, I hope that they will keep coming back.
When I look back on many of the designs I have made, it is always the clearer, simpler ones that I like the most. I take the approach that no amount of design “fairy dust” can transform a poor content site into a successful site. So, I work with clients on ensuring that content always come first.
Working from content outward is always better than trying to “shoe horn” content into a preconceived design or layout, and that is why I often spend more time on planning and wireframing a site before I contemplate the design look-and-feel.
Q: Any last words you’d care to share with us?
A: “When I didn’t know what color to put down, I put down black. Black is a force: I depend on black to simplify the construction.” Actually not my words, but those of artist Henri Matisse.
Summary
Well, that was something of a whirlwind, wasn’t it? With a heightened understanding of media-specific CSS and three different style-switching strategies, we’ve covered no small amount of ground. Yet, as with much of this book, this is but a road map to some incredibly rich landscapes. As a result, it’s going to feel like quite a gloss. We could cover an entire chapter on each of these topics, and would recommend further, deeper research on any of these CSS switching strategies. Our users will thank us if we do that, our sites will be available to a much wider audience, and we’ll be able to tell our mother that we actually wrote something useful.
And to that end, it’s time to stop seeing us walk the walk—in the next chapter, we talk all kinds of talk. By applying the techniques discussed throughout the book, it’s time to finally put these strategies to practice in a real-world site design. Exciting, yes?
Got something to say?
Share your comments with other professionals (8 comments)
Related Topics: CSS, Scripting, PHP, XHTML
Christopher Schmitt is a owner of the Heatvision.com, Inc., a new media design and publishing firm. He’s the list mom for the Web design and development mailing list, Babble. Christopher’s personal site is at http://christopher.org/.
Mark Trammell is the university Web administrator at the University of Florida in Gainesville, Fla. When not attending college athletic events with his wife, Kaye, he occasionally posts to Chasing Function.
Ethan Marcotte is a web designer/developer/something situated in Cambridge, MA. He blogs intermittently as the curator of sidesh0w, and spends much of his time thinking that the float model was a pretty neat idea. Ethan is also the design lead at Vertua Studios, and would like to be an unstoppable robot ninja when he grows up.
Todd Dominey is the founder of Dominey Design in Atlanta, GA, and Senior Interactive Designer with Turner Sports Interactive. He likes his martinis dry, his cats fat, his lawn weed free and code that works the very first try.
Dunstan Orchard is Senior UI Engineer for Apple’s online store during the day. In his spare time he’s the UI Guy for podcasting-startup Odeo. You can read more about him 1976 design.