Technique C29:Using a style switcher to provide a conforming alternate version

Applicability

CSS used with client-side or server-side scripting.

This technique is not referenced from any Understanding document.

Description

When some aspect of the default presentation of a Web page does not meet a Success Criterion, it is possible to meet that requirement using the "Alternate Version" clause in the conformance requirements (Conformance Requirement 1). For some requirements, invoking a style switcher via a link or control on the page that can adjust the presentation so that all aspects of the page conform at the level claimed allows authors to avoid having to provide multiple versions of the same information.

The objective of this technique is to demonstrate how CSS can be used in combination with scripting to provide conforming alternate versions of a Web page. In this technique, an author provides alternative views of the content by providing controls that adjust the CSS that is used to control the visual presentation of content. Controls provided within the Web page allow users to select or modify the presentation in a way that meets the success criterion at the level claimed. This makes it possible for different visual presentations to be selected by users in situations such as the following:

  • the user may not be able to adjust browser or operating system settings, due to a lack of familiarity or rights
  • the text is provided in a manner that does not respond to browser or operating system settings (such as text within an image)
  • the default presentation of the content does not include sufficient contrast for some users

For this technique to be used successfully, three things must be true.

  1. The link or control on the original page must itself meet the success criterion to be met via the alternate presentation. For example, if a style switcher is used to provide increased font sizes and the control is presented using a small font, users may not be able to activate the control and view the alternate presentation.
  2. The new page must contain all the same information and functionality as the original page.
  3. The new page must conform to all of the Success Criteria for the desired level of conformance. For example, an alternate stylesheet can not be used to meet one requirement if it causes a different requirement to no longer conform.

When using a style switcher, it is important to consider the following challenges and limitations:

  • The number and type of changes that a user can make is limited to the scope of the controls provided by the author of the Web page. A variety of presentation and preferences should be provided in order to address the needs of as wide an audience as possible. However, it is also important for authors to consider interactions between preferences and the complexity for users that might result from providing large numbers of options to users.
  • Maintaining the user's preference from one page to the next may be achieved by storing a cookie on the user's machine (see Resources section for more information) or by including their preferences in a profile saved on the Web server by passing a query string parameter, or by other means.
  • The technical method used to implement a style switcher may be subject to the support and availability of one or more technologies on the user's machine (for example, many client-side solutions require support for both JavaScript and CSS). Unless these technologies are relied upon for conformance, authors should consider using server-side technologies where client-side support and availability of technologies can not be assured. Alternatively, the use of techniques which ensure that content will transform gracefully when one or more of the technologies used are not available can be an effective way to enhance pages when support for these technologies is not relied upon for conformance.

Examples

Example 1: Using a JavaScript control to apply a different external CSS file

This example is of a page that provides links to change text and background colors for the page via JavaScript. The links should only be inserted if JavaScript is supported by and available on the user's system. Otherwise, selecting the links will not result in the desired changes. This can be achieved by using script to insert the links themselves (which means that the links would only be present when scripting is supported and available).

The following code shows the JavaScript-dependent color-change links and a snippet of other content in the Web page, the associated style sheet rules, and the JavaScript that changes the style sheet in use when a color-change link is selected.

The example applies only to the current page view. In a production environment, it would be advisable to save this preference in a cookie or server-side user profile, so that users would only have to make the selection once per site.

The XHTML components:

In <head> section:

  <link href="main.css" rel="stylesheet" type="text/css" />
  <link id="currentCSS" href="defaultColors.css" rel="stylesheet" type="text/css" />

In <body> section:

<div id="colorswitch">
<p>Change colors:</p>
  <ul class="inline">
    <li><a href="#" onClick="javascript:changeColors('altColors1.css');return false;" 
      id="altColors1">dark blue on white</a></li>
    <li><a href="#" onClick="javascript:changeColors('altColors2.css');return false;" 
      id="altColors2">yellow on black</a></li>
    <li><a href="#" onClick="javascript:changeColors('altColors3.css');return false;" 
      id="altColors3">black on pale yellow</a></li>
    <li><a href="#" onClick="javascript:changeColors('altColors4.css');return false;" 
      id="altColors4">black on white</a></li>
    <li><a href="#" onClick="javascript:changeColors('defaultColors.css');return false;" 
      id="default">Reset to default</a></li>
  </ul>
</div>
<div id="mainbody">
  <h1>Conference report</h1>
  <p>Last week's conference presented an impressive line-up of speakers...</p>
</div>

The CSS components:

In main.css:

body{ font-family: Geneva, Arial, Helvetica, sans-serif; margin: 2em; }

#mainbody { 
    padding: 1em; 
}

#colorswitch {
    float: right; 
    width: 12em; 
    border: 1px #000066 solid; 
    padding:0 1em 1em 1em; margin:0;
}

#colorswitch p { 
    padding-top:.5em; 
    font-weight:bold;
}

In defaultColors.css:

body, p { 
    color:#000000; 
    background-color:#FFFFFF; 
}

h1, h2, h3 {
        color:#990000; 
        background-color:#FFFFFF;
}

In altColors1.css:

body, h1, h2, h3, p, a { 
    color:#000066; 
    background-color:#FFFFFF; 
}

In altColors2.css:

body, h1, h2, h3, p, a { 
    color:#FFFF33; 
       background-color:#000000; 
}

In altColors3.css:

body, h1, h2, h3, p, a { 
    color:#000000; 
    background-color:#FFFF99; 
}

In altColors4.css:

body, h1, h2, h3, p, a { 
    color:#000000; 
    background-color:#FFFFFF; 
}
  

The JavaScript components:

function changeColors (newCSS)
{
  document.getElementById('currentCSS').href = newCSS; 
}

A working example of this code, Using a JavaScript control to apply a different external CSS file, is available.

Example 2: Using a client-side JavaScript to change a CSS property

This example can be used for simple changes to a section of content and may be less practical for complex sites or pages. The example uses a client-side JavaScript to change the class name to visually present the user's color selection (from a defined set of options) as a background for highlighting specific content.

The following code includes JavaScript calls within the XHTML code to aid understanding of the technique. However, the author is encouraged to use current best practice for including JavaScript (see resources for more information about Unobtrusive JavaScript and progressive enhancement).

The XHTML components:

<h1>Product comparison</h1>
<p>The products you selected to compare are listed below. 
Any differences between the products are highlighted and italicized.</p>
<p class="inlinePara">Change hightlight color: </p>
<ul class="inline">
  <li><a href="#" onClick="changeColor('hghltLightYellow');return false;" 
    class="hghltLightYellow">light yellow</a></li>
  <li><a href="#" onClick="changeColor('hghltBrightYellow');return false;" 
    class="hghltBrightYellow">bright yellow</a></li>
  <li><a href="#" onClick="changeColor('hghltLightBlue');return false;" 
    class="hghltLightBlue">light blue</a></li>
  <li><a href="#" onClick="changeColor('hghltBrightBlue');return false;" 
    class="hghltBrightBlue">bright blue</a></li>
  <li><a href="#" onClick="changeColor('hghltLightRed');return false;" 
    class="hghltLightRed">light red</a></li>
  <li><a href="#" onClick="changeColor('hghltDrkRed');return false;" 
    class="hghltDrkRed">dark red</a></li>
</ul>
<table width="400" border="1">
  <tr>
    <td> </td>
    <th scope="col">Product 1</th>
    <th scope="col">Product 2</th>
  </tr>
  <tr>
    <th scope="row">Aspect 1</th>
    <td>Yes</td>
    <td>Yes</td>
  </tr>
  <tr>
    <th scope="row">Aspect 2</th>
    <td class="hghltLightYellow">Yes</td>
    <td class="hghltLightYellow">No</td>
  </tr>
  <tr>
    <th scope="row">Aspect 3</th>
    <td>Yes</td>
    <td>Yes</td>
  </tr>
</table>

The CSS components:

body { color:#000000; background-color:#FFFFFF; }

.hghltLightYellow { color: #000000; background-color: #FFFF99; font-style:oblique; }
.hghltBrightYellow { color: #000000; background-color: #FFFF00; font-style:oblique; }
.hghltLightBlue { color: #000000; background-color: #33FFFF; font-style:oblique; }
.hghltBrightBlue { color: #FFFFFF; background-color: #0000FF; font-style:oblique; }
.hghltLightRed { color: #000000; background-color: #FF6266; font-style:oblique; }
.hghltDrkRed { color: #FFFFFF; background-color: #993300; font-style:oblique; }

.inlinePara {display:inline; }
.inline {display: inline; margin-left:0px; padding-left:0px; line-height:3em; }
.inline li { display:inline; }
.inline li a {padding: 0.5em 1em; border: 2px solid #000000; }

The JavaScript components:

function changeColor(hghltColor)
{
  // collects table data cells into an array
 
 var els = document.getElementsByTagName('td');

  // for each item in the array, look for a class name starting with "hghlt"
  // if found, change the class value to the current selection
  // note that this script assumes the  'td' class attribute is only used for highlighting

  for (var i=0; i<els.length; i++)
  {
    if (els[i].className.indexOf("hghlt") == 0) { els[i].className = hghltColor; }
  }
}

A working example of this code, Using a client-side JavaScript to change a CSS property, is available.

Example 3: Using PHP $_GET to apply a different external CSS file

This simple example uses PHP $_GET to assign one of two available external style sheets. Similar functionality could be achieved using a variety of PHP features. The example applies only to the current page view. In a production environment, it would be advisable to save this preference in a cookie or server-side user profile, so that users would only have to make the selection once per site.

The following code is PHP, but a similar approach would work with a variety of server-side technologies.

The PHP and XHTML components:

At the beginning of the PHP page:

<?php
$thestyle = $_GET['set'];
if ($thestyle == "style1")
	{
	$thestyle = "style2";
	}
else
	{
	$thestyle = "style1";
	}
?>

In the <head> section:

   <link rel="stylesheet" type="text/css" media="screen" href="<?php echo($thestyle);?>.css" >

In <body> section:

<?php
if ($thestyle == "style1") {
	echo "<a href=\"index.php?set=style1\">Switch to Style Sheet Two</a>";
	}
else {
	echo "<a href=\"index.php?set=style2\">Switch to Style Sheet One</a>";
	}
?>

<div id="mainbody">
  <h1>Conference report</h1>
  <p>Last week's conference presented an impressive line-up of speakers...</p>
</div>

The CSS components:

In style1.css:

  body, p { color:#000000; background-color:#FFFFFF; }
  h1, h2, h3 {color:#990000; background-color:#FFFFFF; }

In style2.css:

  body, h1, h2, h3, p, a { color:#FFFF00; background-color:#000000; }

A working example of this code, Using PHP $_GET to apply a different external CSS file, is available.

Example 4: Using JSP to provide an alternative style sheet

The example below uses two files

  • a Java Server Page (JSP) with the form and the form processing code, and
  • an include file with functions used by the previous page and in other pages use the same style.

The server-side code outputs a normal link element for the stylesheet that the user chooses and link elements with "alternate stylesheet" for the other styles. The code can thus be used as a fallback for the client-side code in the second example.

The JSP page with the form:

 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
 %><%@include file="_jsp/styleswitch.jsp"%><?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
   "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
 <head>
   <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
   <title>Change Style</title>
   <%
     String fStyle = "";
     String styleName = "style";
     String defaultStyle = "default";
     Cookie[] cookies = request.getCookies();
 
     // get style from post request parameters
     if (request.getMethod().equals("POST") && request.getParameter("styleSelect") != null) {
       fStyle = request.getParameter("styleSelect");
       // code that validates user input (security) not shown
       
       if (fStyle.equals("nostyle")) { // user prefers no author style
       } else { // user requests author style
         out.println(createStyleLinks(fStyle).toString());
       }
       
       storeStylePreferenceCookie(request, response, fStyle);
     } else if (request.getMethod().equals("GET")) { 
     // GET request; get style from cookie; else default style links
       // get style from cookie
       if (cookies != null) {
         // get style from cookies
         fStyle = getStyleFromCookies(cookies);
 
         if ( !fStyle.equals("NULL_STYLE") ) { // user requests author style
             out.println(createStyleLinks(fStyle).toString());
         } else { // no cookie for style; process request for style preference
           // default style links
           out.println(createStyleLinks(defaultStyle).toString());
         }
       } else { // GET request without cookies: default styles
         out.println(createStyleLinks(defaultStyle).toString());
       }//end else cookies
     }
   %>
 </head>
 <body id="home">
   <form action="_styleSwitch.jsp" method="post" id="styleswitchform" name="styleswitchform">
     <p><label for="styleSelect">Select style: </label>
       <select id="styleSelect" name="styleSelect">
         <option value="default">Default (shades of green)</option>
         <option value="wonb">White on black</option>
         <option value="bonw">Black on white</option>
       </select>
       <input type="submit" value="Change Style" />
     </p>
   </form>
 </body>
 </html>
 

The styleswitcher.jsp file included in the previous file:

 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 <%!
   /**
    * Get the links (link elements) to the CSS files based on cookies, ...
   */
   private String getStyleLinks(HttpServletRequest request) {
     String styleLinks = "";
     Cookie[] cookies = request.getCookies();
     String defaultStyle = "default";
     String tempStyle = "";
     // GET request; get style from cookie; else default style links
     // get style from cookie
     if (cookies != null) {
       // get style from cookies
       tempStyle = getStyleFromCookies(cookies);
 
       if ( tempStyle.equals("NULL_STYLE") ) { 
         // no cookie for style; process request for style preference
         // default style links
         styleLinks = createStyleLinks(defaultStyle).toString();
       } else { // user requests author style
         styleLinks = createStyleLinks(tempStyle).toString();
       }
     } else { // GET request without cookies: default styles
       styleLinks = createStyleLinks(defaultStyle).toString();
     }//end else cookies
     
     return styleLinks;
   }
 
   /**
    * Get style cookie from request
   */
   private String getStyleFromCookies( Cookie[] cookies ) {
     String fStyle = "NULL_STYLE";
     for (int i = 0; i < cookies.length; i++) {
       Cookie cookie = cookies[i];
       String name = cookie.getName();
       
       if ( name.equals("style") ) {
         fStyle = cookie.getValue();
         // code that validates cookie value (security) not shown
       }
     }
     return fStyle;
   }
 
   /**
    * Store the style preference in a persistent cookie
   */
   private void storeStylePreferenceCookie(HttpServletRequest request, 
     HttpServletResponse response, String theStyle) {
     final int ONE_YEAR = 60 * 60 * 24 * 365;
     Cookie styleCookie = new Cookie("style", theStyle);
     styleCookie.setMaxAge(ONE_YEAR);
     response.addCookie(styleCookie);
   }
 
   /**
    * Create the link elements for the stylesheets
   */
   private StringBuffer createStyleLinks(String prefStyle) {
     StringBuffer theStyleLinks = new StringBuffer();
     //two-dimensional array with identifiers (adding '.css' gives the name of the CSS file) 
     // and strings for the title attribute of the link element
    // the identifiers must correspond to the in the "value" attributes in the "option"
    // elements in the style switcher form
     String [] [] styles = {
       { "default", "Default style"},
       { "wonb", "White on black"},
       { "bonw", "Black on white"}
     };
 
     // loop over 2dim array: if styles[i][1] matches prefStyle, 
     // output as normal, else as alternate stylesheet
     for (int i = 0; i < styles.length; i++) {
       if ( styles[i][0].equals(prefStyle) ) { // output pref stylesheet as normal stylesheet
         theStyleLinks.append("<link rel=\"stylesheet\" href=\"_css/").append(styles[i][0])
           .append(".css\" title=\"").append(styles[i][1]).append("\" type=\"text/css\" />").append("\n");
       } else { // output other stylesheets as alternate stylesheets
         theStyleLinks.append("<link rel=\"alternate stylesheet\" href=\"_css/")
           .append(styles[i][0]).append(".css\" title=\"").append(styles[i][1])
           .append("\" type=\"text/css\" />").append("\n");
       }
     } // end for loop
 
     return theStyleLinks;
   }
 %>
 

Other JSP pages can use this code by means of the following include and scriptlet code:

 <%@include file="_jsp/styleswitch.jsp"%><% out.println(getStyleLinks(request)); %>
 

Other sources

No endorsement implied.

Using cookies

A user's selection can be made persistent across pages, and from one visit to another, by storing information on the user's computer via a cookie. This functionality requires cookies to be supported by and allowed on the user's computer. Cookies can be created, read, modified and erased by using client-side scripts, such as Javascript, or by server-side scripts, such as CGI scripts. Reliance on client-side technologies will require the support and availability of the technology on the user's computer in addition to supporting and allowing cookies.

Information on creating and using cookies can be found on the web. Here are some suggestions:

It is recommended that authors test for cookie support and provide an extra control if cookies are not supported. This extra control should include information about the persistence of the selection, such as "Apply selection to all pages". The message or page presented to the user in response to selecting the extra control provides information about the cookie requirement and their options for solving it. In the event that the user is unable to turn cookie support on, include a statement about what this will mean for them if they choose to continue to browse the site and provide information about how they can adjust their user agent to achieve similar results.

For example, "Your browser is not configured to accept cookies. On this site, cookies are required in order to apply your selected changes across all of the pages of the site. To find out how to enable cookies on your computer, visit How to Enable Cookies. Note that this may require administrative rights for the computer you are using. Without cookie support, your settings will not persist to include other pages on this site. We are endeavoring to provide this functionality without relying on your computer's capability. In the meantime, you will be able to select the change for each page that you visit."

Progressive Enhancement and Unobtrusive Javascript

Current best practice for implementing JavaScript in an HTML or XHTML page is to use it in a way that separates the behavior of content from its structure and presentation. The terms 'Progressive Enhancement' and 'Unobtrusive JavaScript' are often used to describe scripts that enhance or improve the functionality of a page, yet transform gracefully so that content continues to function even when JavaScript is not supported.

Here are some suggested starting points for more information:

Tests

Procedure

  1. Check that the Web page contains controls that allow users to select alternate presentations.
  2. Check that the control changes the presentation by modifying individual CSS style properties or by activating an alternate style sheet.
  3. Verify that the resulting page is a conforming alternate version for the original page.

Expected Results

  • All of the above checks are true.