Skip navigation

design: personality switcher

October 17, 2004 Confuse your friends! Drive visitors away scratching their heads! Change personalities and font sizes on your website for fun and, well, fun. Here's how we do it here at deep gray sea. But first, a small joke:

Q: How many psychiatrists does it take to change a light bulb?
A: Only one, but it has to really want to change.

The personality switcher is a bit like that. One web designer can do it, but you have to really want to. It's a ton of work.

how it works in a nutshell

Each page in the site includes several stylesheets, in two sets. One of the sets is concerned with personality - color, layout, graphics, and so on. The other set is concerned exclusively with font sizes. Font sizes and layouts aren't intermingled in the same stylesheet so they may be independently selected.

JavaScript functions fire both when the page loads and when it exits, reading or saving cookies that remember the visitor's preference for style and text size. Links are placed on each page so you can play while traversing the site; the links also fire JavaScript functions that enable and disable the correct stylesheets.

some design considerations

A design challenge that surprised me when developing the site was how hard it was to select font sizes that work for all layouts and type faces. Whenever I'd toy with a new personality, I'd go back and second-guess font size choices all over again.

I'm sort of obsessed by the idea of using tiny photos as icons at the moment. But the icons I use need to change for the different personalities - different images, sizes, backgrounds, borders, etc. That meant that I needed to set those values in the stylesheet, not in the page itself.

The great-granddaddy multiple personality site out on the web is CSS Zen Garden. Many of the layouts there substitute graphics for text, especially for section headers. I thought about doing that, but decided it would be a lot of trouble maintaining a growing content site if I had to come up with a small handful of differently formatted graphics every time I created a new page. So I made the rather lazy decision to just use text instead. If the design was for only one page, I might have chosen differently.

loading the stylesheets

At the moment, the site has three personalities and three different text sizes. Here are the stylesheet include statements that appear on each page:

<!-- personality stylesheets -->
<link class="personality" rel="stylesheet" type="text/css" 
  href="z.css" media="all" /><!-- default style sheet -->
<link class="personality" rel="alternate stylesheet" type="text/css" 
  href="uahanai.css" media="all" title="uahanai" />
<link class="personality" rel="alternate stylesheet" type="text/css" 
  href="candle.css" media="all" title="candle" />
<!-- font size stylesheets -->
<link class="fontsize" rel="stylesheet" type="text/css" 
  href="zsmall.css" media="all" title="smallest" /><!-- default style sheet -->
<link class="fontsize" rel="alternate stylesheet" type="text/css" 
  href="zmedium.css" title="medium" />
<link class="fontsize" rel="alternate stylesheet" type="text/css" 
  href="zlarge.css" title="largest" />

The class attributes are used to identify the type of stylesheet we're dealing with - this is used in scripting later. The rel attributes identify each stylesheet as either a default or alternate. The default stylesheets are loaded by the page, the others are disabled until we say otherwise in script. There aren't any special print or mobile stylesheets on the site (yet).

loading the scripts

The pages each load a couple of scripts. One stores data about pot-scrubbers and so on for the Amazon product switcher. The other is core.js, which contains the style-swapping code and other utilities.

<script type="text/javascript" src="core.js"></script>
<script type="text/javascript" src="design.js"></script>
initialization

The startup code from core.js below gets called when the page loads. The initStyles() function fires immediately so the correct stylesheet will be active as the page renders. If you wait until the onload event fires to do this, you'll see the page load the default style first (I found this out the hard way). The functions that highlight the personality and text size icons use global variables we'll learn about in a minute.

initStyles(); /* fires immediately */

function init() {
  setPersonalityIcon(myPersonality);
  setTextSizeIcon(myFontSize);
  initAmazon();
}
window.onload = init; /* fires after page load */
window.onunload = writeStylePreferences; /* write style prefs on unload */

The initStyles() function attempts to read two cookies stored on your computer. One stores your personality preference, the other your text size preference. If a cookie is found, great, if not, a default value is chosen by the getPreferredStyleSheet() function. The stored preferences are saved as globals (declared near the top of the file) so we only have to read the cookies once per page.

function initStyles() {
  var cookie = readCookie("personality");
  var title = cookie ? cookie : getPreferredStyleSheet("personality");
  myPersonality = title; /* global */
  setActiveStyleSheet(title, "personality");
  cookie = readCookie("fontsize");
  title = cookie ? cookie : getPreferredStyleSheet("fontsize");
  myFontSize = title; /* global */
  setActiveStyleSheet(title, "fontsize");
}

The cookie value is passed as an argument in the setActiveStyleSheet() function call. Note the second argument matches the classnames assigned to our stylesheets. Here's what this function looks like:

function setActiveStyleSheet(title, theclass) {
  var i, a, main;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1 
       && a.getAttribute("title")
       && a.className == theclass) {
      a.disabled = true;
      if(a.getAttribute("title") == title && a.className == theclass) a.disabled = false;
    }
  }
}

The function loops through all the stylesheets loaded by the document that match the classname. Any that don't also match on title are disabled.

The icons are highlighted from the onload call, using the global variables we set earlier. This is done by setting the class name of the appropriate link in the menu through a call to the setPersonalityIcon() and setTextSizeIcon() functions:

function setPersonalityIcon(title) {
  document.getElementById("candlelink").className = (title == "candle" ? "persiconthis" : "");
  document.getElementById("uahanailink").className = (title == "uahanai" ? "persiconthis" : "");
  document.getElementById("defaultlink").className = (title == "default" ? "persiconthis" : "");
}

function setTextSizeIcon(title) {
  document.getElementById("smtextlink").className = (title == "smallest" ? "texticonthis" : "");
  document.getElementById("medtextlink").className = (title == "medium" ? "texticonthis" : "");
  document.getElementById("lgtextlink").className = (title == "largest" ? "texticonthis" : "");
}
changing styles

Selecting a new personality or text size goes through pretty much the same process. Clicking a link fires the setActiveStyleSheet() function with specific values passed. The text size links are shown below:

<ul id="textsizemenulinks">
  <li><a href="multiplepersonalities.htm" id="smtextlink" title="smallest text" 
    onclick="setActiveStyleSheet('smallest', 'fontsize');"></a></li>
  <li><a href="multiplepersonalities.htm" id="medtextlink" title="medium text" 
    onclick="setActiveStyleSheet('medium', 'fontsize');"></a></li>
  <li><a href="multiplepersonalities.htm" id="lgtextlink" title="largest text" 
    onclick="setActiveStyleSheet('largest', 'fontsize');"></a></li>
</ul>

One other wrinkle you may notice is the page value in the href attribute. This reloads the page after swapping the stylesheets. This sets the new icon indicators and also helps prevent quirky browser behavior from, well, quirky browsers.

credits

The style swapper used here benefits greatly from the Alternative Style: Working With Alternate Style Sheets article written by Paul Sowden for A List Apart. Thanks Paul!

D
D