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
- Persistent Style Sheets
- Preferred Style Sheets
- Alternate Style Sheets
- The Proposed Technique
- The ASP Document
- The CSS Documents
- Limitations
A Russian translation for this artucle has kindly been provided by Everycloud.
About Style Sheet Switching
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.
[Back to the table of contents]
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"/>
[Back to the table of contents]
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.
[Back to the table of contents]
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"/>
[Back to the table of contents]
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.
[Back to the table of contents]
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 %>
[Back to the table of contents]
The CSS Documents
There are three external style sheets used in this example.
- main.css - the persistent style sheet.
- nofloat.asp - the default preferred style sheet.
- leftstyle.asp - the default alternate style sheet.
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;
}
[Back to the table of contents]
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.
[Back to the table of contents]
Category: Style.
[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
[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
[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
[stylesheet-switcher.php#comment4]
Great idea, nicely coded. Why don't you use it on this site?
Posted by Zoe on
[stylesheet-switcher.php#comment5]
very nice indeed
Posted by nutter9k on
[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
[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
[stylesheet-switcher.php#comment8]
Nice technique, shame about the name
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