Summary

During a recent discussion with Jim Byrne of the Making Connections Unit, we pondered over the usefulness of scripts that allow visitors to change their preferences. We agreed we had reservations over their use for the following reasons.

  • Educating users how to use their particular user agent is the ideal, such as resizing text. Most modern browsers allow the text size to be changed easily through the browser.
  • Most scripts are written in JavaScript, and so rely on the client having scripting capabilities.
  • Lots of sites that use these types of features implement them as icons, which get overlooked by users that would benefit from them.
  • They're usually implemented as fixed units, for example 9px or 12px, which actually hinders those that do know how to use their user agents, and are typically too limited in the choices they provide.

Jim came up with the idea of providing a more flexible interface that allows visitors to select relative sizes for fonts, and a choice of foreground and background colour combinations using a server side script. Jim's ideas adequately deal with our initial reservations. Discussing the idea further, we investigated ways that this technique could be incorporated into style sheet switchers.

Author: Gez Lemon

Contents

About Style Sheet Switching

Thanks to Mario Pozner for a Ukraine translation of this article.

Style sheet switching is a mechanism provided by modern browsers that allow the user to choose between style sheets provided by the author. Browsers such as Gecko based browsers allow the user to choose which style they wish to use through the menu system. Internet Explorer, often quoted as being used by over 95% of surfers, does not provide a mechanism to switch style sheets. As not all browsers support style sheet switching, some content developers provide their own mechanism for the style sheets to be changed. In an ideal world, we wouldn't have to resort to these measures. As we're likely to be stuck with the current version of Internet Explorer as the most common browser for at least the next three years, we can improve usability by providing a mechanism for our visitors to customise their experience.

There are several published scripts for dealing with style sheet switching. A List Apart has two excellent scripts illustrating style sheet switching in JavaScript by Paul Sowden, and style sheet switching in PHP by Chris Clark. We decided to incorporate Jim's ideas into switchable style sheets, for maximum flexibility. Before we examine the code, we'll look at how style sheets are linked to a document as persistent, preferred, and alternative style sheets.

Persistent Style Sheets

A persistent style sheet is always enabled. If the rules for a document are contained in a single style sheet, then this is the way you will link to the style sheet. If the document has more than one style sheet associated with it, the basic rules may be placed in this style sheet. The relationship is specified with a rel attribute value of, "stylesheet", and no title attribute is provided.

<link href="persistent.css" rel="stylesheet" type="text/css"/>

Preferred Style Sheets

A preferred style sheet is enabled by default, but may be switched by the user for an alternate style sheet. A preferred relationship is specified with a rel attribute value of, "stylesheet", and a title attribute is also provided. You may specify more than one preferred style sheet by specifying the same title for each. The preferred style sheets will be grouped, and enabled and disabled together. If more than one group of preferred style sheets are specified, the first group will take precedence over the other groups.

Alternate Style Sheets

An alternate style sheet is one that may be selected by the visitor as an alternative to the preferred style sheet. An alternate relationship is specified with a rel attribute value of, "alternate stylesheet", and a title attribute is also provided. Like preferred style sheets, alternate style sheets may be grouped by giving them the same title.

The following example uses a persistent, preferred, and alternate style sheet, allowing the visitor to customise the look of the site to their taste.

<link href="persistent.css" rel="stylesheet" type="text/css"/>
<link href="preferred.css" rel="stylesheet" title="Olive" type="text/css"/>
<link href="alternate.css" rel="alternate stylesheet" title="Yellow" type="text/css"/>

The Proposed Technique

By providing persistent, preferred, and alternate style sheets with a style switching form, visitors have the opportunity to combine the scripts to add more flexibility on how they customise your pages. As Jim Byrne points out, providing highly flexible customisation on its own may result in an unmanageable number of alternate style sheets. This arrangement provides the benefits from both. Visitors familiar with switching style sheets through their browser may continue to do so, and those that are not familiar, or use a browser with limited switching abilities, also benefit from this technique.

The following sections illustrate how the files are organised to implement this technique.

The ASP Document

In this example, the document is presented as an ASP document, but the principles can be applied to any server side scripting language, as demonstrated in Jim Byrne's PHP example.

The values for colour contrast and layout are loaded from a two-dimensional array. This allows the script to be easily configured, as the required values only need to be changed in the array itself. The script checks if the form has been submitted, and sets any cookies required. The document is associated with three styles sheets, persistent, preferred, and alternate. The preferred and alternate are arranged according to the cookie values. If there are no cookies, the style sheets are arranged in the default order. This allows the style sheets to be switched using the form, or through the browser if the browser supports style sheet switching.

The following is the complete ASP document.

<% @ language="vbscript" %>
<% Option Explicit %>
<% Response.Buffer=True %>
<%
    Dim arContrast(2, 1), arLayout(1, 1), iCounter
    Dim strTextSize, strColourContrast, strLayout

    ' Load contrast options into an array
    arContrast(0, 0) = "value=""default"""
    arContrast(0, 1) = "Default Contrast"
    arContrast(1, 0) = "value=""lowcontrast"""
    arContrast(1, 1) = "Low Contrast"
    arContrast(2, 0) = "value=""highcontrast"""
    arContrast(2, 1) = "High Contrast"

    ' Load layout options into an array
    arLayout(0, 0) = "value=""default"""
    arLayout(0, 1) = "Default Layout"
    arLayout(1, 0) = "value=""floatleft"""
    arLayout(1, 1) = "Left Navigation"

    ' Get any values that are already set
    strTextSize = Request.Cookies("style")("size")
    strColourContrast = Request.Cookies("style")("colour")
    strLayout = Request.Cookies("style")("layout")

    ' Check if any values have been updated
    If UCase(Request.ServerVariables("REQUEST_METHOD")) = "POST" Then
        strTextSize = validateRange(Request.Form("textsize"), 80, 250, "100")
        strColourContrast = Request.Form("colourcontrast")
        strLayout = Request.Form("layout")

        ' Store cookies
        Response.Cookies("style")("size") = strTextSize
        Response.Cookies("style")("colour") = strColourContrast
        Response.Cookies("style")("layout") = strLayout
        Response.Cookies("style").Expires = DateAdd("yyyy", 1, Now)
    End If

    ' Provide default values if not set
    If strColourContrast = "" Then
        strColourContrast = "default"
    End If

    If strLayout = "" Then
        strLayout = "default"
    End If

    ' Validate the range for text size,
    ' with a default value of 100
    strTextSize = ValidateRange(strTextSize, 80, 250, "100")

    ' Add selected to the chosen colour contrast
    addSelected arContrast, strColourContrast
    ' Add selected to the chosen layout
    addSelected arLayout, strLayout
%>
<!DOCTYPE html 
    PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Colour Switcher</title>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=ISO-8859-1"/>
<link href="main.css" rel="stylesheet" type="text/css"/>
<% If strLayout = "default" Then %>
<link href="nofloat.asp" rel="stylesheet" title="Basic" type="text/css"/>
<link href="leftstyle.asp" rel="alternate stylesheet" title="Float Left" type="text/css"/>
<% Else %>
<link href="leftstyle.asp" rel="stylesheet" title="Float Left" type="text/css"/>
<link href="nofloat.asp" rel="alternate stylesheet" title="Basic" type="text/css"/>
<% End If %>
</head>
<body>
<div id="header">
<h1>Style Sheet Switcher</h1>
<p>
If you do not see a change in style after submitting the form, 
please refresh your browser as it means the previous values 
have been cached.
</p>
<form action="<%= Request.ServerVariables("SCRIPT_NAME") %>" method="post">
<fieldset>
<legend>Customise This Page</legend>
<label for="textsize">
Text size (%):
<input id="textsize" type="text" name="textsize" value="<%= strTextSize %>"
                     size="4" maxlength="4" tabindex="1"/>
</label>
<label for="colourcontrast">Colour Contrast 
<select name="colourcontrast" id="colourcontrast" tabindex="2">
<optgroup label="Colour Contrast">
<% For iCounter = 0 To UBound(arContrast) %>
<option <%= arContrast(iCounter, 0) %>><%= arContrast(iCounter, 1) %></option>
<% Next %>
</optgroup>
</select>
</label>
<label for="layout">Layout 
<select name="layout" id="layout" tabindex="3">
<optgroup label="Layout">
<% For iCounter = 0 To UBound(arLayout) %>
<option <%= arLayout(iCounter, 0) %>><%= arLayout(iCounter, 1) %></option>
<% Next %>
</optgroup>
</select>
</label>
<input type="submit" value="Change" tabindex="4"/>
</fieldset>
</form>
</div>
<div id="menu">
<h2 id="contents">Contents</h2>
<p>
Contents for the menu here.
</p>
</div>
<div id="maincontent">
<h2 id="intro">Introduction</h2>
<p>
This page serves as an example of a style sheet switcher, based on 
<a href="http://www.mcu.org.uk/articles/styleswitcher.php">an 
idea from Jim Byrne</a> of the 
<a href="http://www.mcu.org.uk/">Making Connections Unit</a>. 
</p>
</div>
</body>
</html>

<%
' Routine to add the selected attribute to a 
' particular item in a list
Private Sub addSelected(ByRef arList, ByVal strValue)
    Dim iCounter

    For iCounter = 0 To UBound(arList)
        If InStr(arList(iCounter, 0), strValue) Then
            arList(iCounter, 0) = arList(iCounter, 0) & " selected=""selected"""
        End If
    Next
End Sub

' Routine to validate the range for a numeric field
' stored as a string
Private Function validateRange(ByVal strSource, ByVal iLower, _
                               ByVal iUpper, ByVal strDefault)

    If strSource = "" Or Not IsNumeric(strSource) Then
        strSource = strDefault
    ElseIf CInt(strSource) < iLower Then
        strSource = CStr(iLower)
    ElseIf CInt(strSource) > iUpper Then
        strSource = CStr(iUpper)
    End If

    validateRange = strSource
End Function
%>

All pages that are to include the style sheet used in this example would require the following in the head section of the HTML document.


<%
    ' Arrange the style sheets in the head section
    Dim strLayout

    strLayout = Request.Cookies("style")("layout")
    If strLayout = "" Then
        strLayout = "default"
    End If
%>
<link href="main.css" rel="stylesheet" type="text/css"/>
<% If strLayout = "default" Then %>
<link href="nofloat.asp" rel="stylesheet" title="Basic" type="text/css"/>
<link href="leftstyle.asp" rel="alternate stylesheet" title="Float Left" type="text/css"/>
<% Else %>
<link href="leftstyle.asp" rel="stylesheet" title="Float Left" type="text/css"/>
<link href="nofloat.asp" rel="alternate stylesheet" title="Basic" type="text/css"/>
<% End If %>

The CSS Documents

There are three external style sheets used in this example.

main.css

The file main.css is a trivial CSS document to define the styles you want to be used throughout, regardless of switching. The main.css file in this example just contains the following.

#header
{
    padding-bottom: 0.5em;
}

fieldset
{
    padding: 1em;
}

nofloat.asp

The file nofloat.asp is the default preferred style sheet. In other words, if no cookies have been set, or the user chooses this style sheet, it will be used. As it has a .asp file extension, the file will be processed on the server. This works fine with Internet Explorer, and Opera, but Gecko based browsers won't use the file as it doesn't have the correct MIME type. Setting the MIME type to text/css at the start of the document solves this. This file uses a server side include to bring in the values set through the form. This technique saves having to write the same code in each external style sheet, making it easier to maintain.

<% Response.ContentType = "text/css" %>
<!-- #include file="basicstyle.asp" -->
#maincontent
{
    padding: 0.5em;
}

#menu
{
    padding: 0.5em;
    border: #ccc 1px solid;
}

leftstyle.asp

The file leftstyle.asp is the default alternate style sheet. In other words, if no cookies have been set, this style may be selected through browsers that support style sheet switching, or using the form. As with nofloat.asp, this file requires the MIME type to be set, and uses a server side include to bring in the values set in the form.

<% Response.ContentType = "text/css" %>
<!-- #include file="basicstyle.asp" -->
#maincontent
{
    margin-left: 20%;
    padding: 0.5em;
    border-left: #ccc 1px solid;
}

#menu
{
    float: left;
    width: 17%;
    padding: 0.5em;
}

basicstyle.asp

The file basicstyle.asp is the file included in the two alternative style sheets above. Its job is to build the values according to values that have been set through cookies into the style sheets. If no values have been set, then default values are used instead. This file doesn't require a content type, as the content type is set in the files that include it.

<%
    Dim strTextSize, strColourContrast

    ' Get any values that are already set
    strTextSize = Request.Cookies("style")("size")
    strColourContrast = Request.Cookies("style")("colour")

    ' Give default values for values if not provided
    If strColourContrast = "" Then
        strColourContrast = "default"
    End If

    If strTextSize = "" Then
        strTextSize = "100"
    End If
%>

html, body
{
    font-size: <%= strTextSize %>%;

<%
Select Case strColourContrast
    Case "lowcontrast"
        Response.Write "color: #ccc;" & vbCrLf
        Response.Write "background-color: #fff;" & vbCrLf
    Case "highcontrast"
        Response.Write "color: #fff;" & vbCrLf
        Response.Write "background-color: #000;" & vbCrLf
    Case Else
        Response.Write "color: #000;" & vbCrLf
        Response.Write "background-color: #fff;" & vbCrLf
End Select
%>
}

a
{
<%
Select Case strColourContrast
    Case "lowcontrast"
        Response.Write "color: #99c;" & vbCrLf
    Case "highcontrast"
        Response.Write "color: #fc0;" & vbCrLf
    Case Else
        Response.Write "color: #00c;" & vbCrLf
End Select
%>
    background: transparent;
}

Limitations

The major benefit of including CSS from an external style sheet is that the style sheet will be cached. This saves on download time from the server, keeping the HTML documents lighter. Unfortunately, caching may also mean that the values aren't updated immediately. You could configure the CSS pages so that they're not cached, but this defeats the major purpose of organising the files this way. A gentle reminder to refresh the page having made changes helps the visitor understand what's happened.

This technique does require cookies to be enabled, but doesn't depend on them. If cookies aren't enabled, then the values can't be changed using the form. The values can still be set through any browser that supports style sheet switching. If cookies are enabled, then visitors have the choice of using the form or their browser, providing an effective and efficient means of style sheet switching.

Category: Style.

Comments

  1. [stylesheet-switcher.php#comment1]

    Superb!

    That is by far the best style sheet switching script I've seen. The idea of using arrays for the drop down menus is really neat, and almost skipped over in the tutorial. I'll definitely be using that technique in future projects.

    What is the problem with Cache you mention? The demo page worked fine when I tried it.

    Posted by Justin on

  2. [stylesheet-switcher.php#comment2]

    Thanks for the kind comments, Justin. If you try the demonstration using Opera, you'll probably find you'll need to refresh the browser when you change the text size or colour. The layout would work without requiring a refresh, as that's more of a brute force approach.

    Posted by Gez on

  3. [stylesheet-switcher.php#comment3]

    I can only echo Justin's remark. This really is superb. I've attempted dynamic CSS in the past but without success. The example works a treat.

    Great work guys.

    Posted by JRC on

  4. [stylesheet-switcher.php#comment6]

    The Byrne/Lemon Technique, rolls right off your tongue!

    I think it's a great idea, and I can't wait to see you implement it into JS. I will certainly be implementing it into future projects.

    "What will they come up with next"?

    Posted by Keiran on

  5. [stylesheet-switcher.php#comment7]

    It's working well on Juicify, but I'm sill curious to know why you're not using it on this site?

    Posted by Zoe on

  6. [stylesheet-switcher.php#comment8]

    Nice technique, shame about the name *smile*

    I have a project looming that I intended to do as static XHTML. Following this article, and the MIME type article, I'm tempted to go with a server side language.

    Posted by Ed on

Comments are closed for this entry.