This just in: Naming bad fashion

Textpattern plugin: Stylesheet switcher

n: smd_style | v: 0.2 | d: 546 | f: /

Plugin documentation follows this short message from our sponsor ;-)

If you like my code and find it useful, feel free to show your appreciation with something from my UK Amazon wish list (or US) or donate to the Stef Dawson community coding pot by following the Donate button below to PayPal. Thanks!

Plugin list buttonDownload buttonCompressed download button

smd_style

Manage your alternate stylesheets and (optionally) switch between them via Javascript.

Features:

  • Provide a list of style sheet names derived from:
    • a given list
    • any article field
    • a <txp:variable />
    • a URL variable
    • some combination of the above
  • Use either the built-in stylesheet switching ability of modern browsers or the plugin’s javascript switcher
  • Stores current style in a cookie and can be instructed to load that style first on subsequent pages
  • Compatible with TXP stylesheets, ‘real’ stylesheets and the rvm_css plugin
  • Optionally build your own stylesheet syntax via a form/container
  • Put TXP tags inside your stylesheets if you wish (requires TXP-style sheet processing, i.e real_sheets must be off)

Author

Stef Dawson

Installation / Uninstallation

Download the plugin from either textpattern.org, or the software page, paste the code into the TXP Admin -> Plugins pane, install and enable the plugin. Visit the forum thread for more info and to report the success (or otherwise) of this plugin.

To uninstall, simply delete from the Admin -> Plugins page.

Usage

There are two tags available: one for use in the <head> of your page to set up the alternate styles, and the other can optionally add a javascript stylesheet switcher to the page, wherever you want it to appear.

smd_style

Somewhere in your HTML <head> tag, add a call to smd_style to include a list of alternate stylesheets. Use the following attributes to customise the output:

Attributes

  • sheets : a comma-separated list of stylesheets to include. These are their TXP names as given on the Presentation->Styles tab. See sheet names for more
  • form : the TXP form in which to build your own <link> tags. See replacement tags. If not specified (and no container is used) a standard XHTML link will be output
  • use_default : the list of stylesheets given in the sheets parameter does not usually contain the ‘default’ stylesheet associated with the section. This allows you to always include a ‘global’ sheet with core rules in it and have the alternates override/fill in the gaps. If, however, you wish smd_style to output the default stylesheet so you don’t have to do it elsewhere, specify use_default="1"
  • promote_recent : Determine what to do when the page loads and the visitor has already switched to an alternate stylesheet. This can be useful to try and help prevent ‘flashes of unstyled content’ in good browsers. Can take one of three values:
    • 0 : do nothing, i.e. sheets are always loaded in the given order
    • 1 (the default) : any style sheet that has been chosen by the visitor will be loaded as soon as possible after any core sheets (i.e. sheets that you want to be non-alternate). Thus if you have 2 ‘core’ sheets and the visitor chooses the alternate sheet that used to be #4 in the list, it will now be loaded 3rd
    • 2 : any style sheet that has been chosen by the visitor will be loaded first, i.e. the chosen sheet will always be promoted to the #1 slot
  • skip : in the list of sheets, skip this number of sheets before starting to label them as ‘alternate’. The special default setting of auto behaves thus:
    • if use_default="1", the first sheet in the list will be skipped (i.e. will always be a true stylesheet). Equivalent to skip="1"
    • if use_default="0" the first sheet in the list will be an alternate stylesheet (i.e. equivalent to skip="0")
  • skip_titles : in the list of sheets, skip this number of sheets before starting to add title attributes. The special default setting of auto works the same as for the skip attribute. Note that some combinations of skip/skip_title are not permitted by the HTML specification so are disallowed. See skipping for more details
  • show_empty : by default, if any field (e.g. a custom field) has no value assigned to it and you try to read a sheet name, the field will be ignored. Setting this to 1 will include a stylesheet with the name of the field itself. See Example 5 for a practical application of this
  • real_sheets : normally, standard css.php?n=sheet_name references are output. If you prefer ‘real’ stylesheet filename references, set this attribute to 1. Note you will have to ensure that the stylesheet files really exist on your server in the given directory (see sheets_dir)
  • sheets_dir : location (relative to the root of your textpattern installation) where your stylesheets are to be found if using real_sheets="1". If you have the rvm_css plugin installed it will default to the directory specified in Admin -> Preferences -> Advanced -> Style directory
  • parse_tags : if you wish to be able to put TXP tags inside your style sheets and have them parsed, you now can. Set this attribute to 1 to enable tag parsing. Note that this only works if real_styles="0" because there’s no way for Textpattern to intercept a direct call to a stylesheet
  • delim : the delimiter to use between sheets. Default is the comma (,)
  • param_delim : the delimiter to use between additional sheet information. Default is the colon (:)

Sheet names

When using the sheets attribute, if you precede any sheet name with a ? the TXP environment will be searched for a matching location. For example, sheets="core, ?custom3" would load core.css and then look in the custom3 field for a further list of stylesheets to load. The order in which the locations are searched is: 1) Article fields; 2) <txp:variable />; 3) The URL. If a ? value is supplied and a suitable location cannot be found, the value you gave will be used verbatim.

You may also specify up to two additional pieces of information in each sheet definition: 1) the sheet’s “pretty name” that people will see, and 2) its media type (usually screen). To do this, separate the values with colons (unless changed via the param_delim attribute) like this:

<txp:smd_style use_default="1"
     sheets="core, cats:Cats of the world, printer:Print layout:print" />

That would output something similar to the following:

<link rel="stylesheet" type="text/css" media="screen"
     title="default" href="http://site.com/textpattern/css.php?n=default" />
<link rel="alternate stylesheet" type="text/css" media="screen"
     title="flowers" href="http://site.com/textpattern/css.php?n=flowers" />
<link rel="alternate stylesheet" type="text/css" media="screen"
     title="Cats of the world" href="http://site.com/textpattern/css.php?n=cats" />
<link rel="alternate stylesheet" type="text/css" media="print"
     title="Print layout" href="http://site.com/textpattern/css.php?n=printer" />

Replacement tags

If you don’t like the standard output or wish to fashion your own <link> tags then you can specify container content or a form with which to process the results. There are replacement variables you can employ within your layout to insert the relevant content for each stylesheet:

  • {smd_style_name} : the stylesheet name as given in the TXP Styles tab
  • {smd_style_url} : the complete URL (TXP-style or ‘real’ depending on the real_sheets attribute)
  • {smd_style_media} : the type of stylesheet (e.g. screen, print…)
  • {smd_style_rel} : the stylesheet relationship (either ‘stylesheet’ or ‘alternate stylesheet’)
  • {smd_style_title} : the human-friendly title you have assigned to the styelsheet. If not specified, it defaults to the stylesheet name
  • {smd_style_counter} : the current stylesheet number being processed
  • {smd_style_total} : the total number of stylesheets in the list

Skipping

The HTML specification allows for three types of stylesheet:

  • Persistent : ones that are forced on the user unless they choose No Styles from their browser menu
  • Preferred : ones that are always loaded but can be swapped out for others
  • Alternate : ones that are not loaded by default but can be switched in and out freely by the user

With a combination of use_default, skip and skip_title you can offer any of these types. Some combinations of skip and skip_title are not permitted because they would render illegal HTML (e.g. the case when you have an alternate stylesheet without a title).

To try and make it a little easier to visualise, here is a table that shows which of the stylesheet flavours are served with varying values of these attributes. Assuming you have two stylesheets labelled A and B, sheet A will be set to one of the following flavours:

  skip=0 skip=1
skip_title=0 Alternate Preferred
skip_title=1 (disallowed) Alternate Persistent

The same logic applies if using the auto parameter and/or the use_default attribute. The first sheet in the list will become one of those flavours of Stylesheet where the values of skip and skip_title intersect. If using values of ‘N’ or more, the first ‘N’ sheets become those particular flavours.

smd_styleswitch

Requires jQuery to be loaded on your page, before the call to smd_styleswitch. Tested with the latest version (1.2.6 as of this writing).

On its own the smd_style tag gives the visitor the ability to switch styles via the ‘View alternate stylesheet’ facility of modern browsers. If you wish to offer them a way of instantaneously switching (and remembering) the chosen theme from within your content, use smd_styleswitch somewhere in the flow of your page to insert a stylesheet switcher. It can be customised as follows:

Attributes

  • wraptag : standard (X)HTML tag to wrap the entire switcher with. Default is ul. If you do not specify this attribute you_must_ create your own container somewhere else in the page
  • class : CSS class to apply to the wraptag. Default is smd_switcher. If you have not specified a wraptag, your own container must be given this class name so the switcher can be inserted
  • break : standard (X)HTML tag to wrap each stylesheet name and link with. Default is li. Note that this does not have to operate as a container tag (see the break_is_tag attribute)
  • break_is_tag : if the break attribute is a tag, this should be set to 1 (which it is by default). If you wish to use something else between each stylesheet name (e.g. break=" | ") set this to 0
  • linkclass : CSS class to apply to each stylesheet link. Default is smd_styleswitch
  • activeclass : CSS class to indicate the currently selected stylesheet link. Default is smd_currstyle
  • alt_only : if set to 1 will only offer stylesheets with a rel that includes ‘alternate’ in the list. The default behaviour (0) is to list all stylesheets on the page that contain a ‘title’ attribute
  • sort : set to 1 (the default) to sort the stylesheets in alphabetical order. If set to 0, the most recently used stylesheet will be shown at the head of the list
  • case_sensitive : whether the sort is case sensitive (1) or not (0). Default is 0
  • expiry : number of days after which the cookie that holds the current stylesheet remains valid. Default is 7 days

The stylesheet switcher that is inserted at the given location consists of an anchor tag with a link to the new stylesheet. The text within the anchor is the stylesheet title (if supplied) or its name if not. Note that the switcher will pick up all stylesheets that have a title attribute so if any of your other stylesheets not controlled by the plugin have titles, they will be included as well. This could be considered a feature(!)

If you are a neatness buff and prefer that all javascript goes in the <head>, using wraptag="" will allow you to insert the <txp:smd_styleswitch /> tag in the head of your document while locating your switching container elsewhere.

Examples

Example 1

<txp:smd_style
     sheets="yellow, green, brown, blue, pink, black" />
<txp:smd_styleswitch wraptag="" />

Somewhere further down the page you would have to add:

<ul class="smd_switcher"></ul>

That will add six alternate stylesheets to the page and add a switcher wherever your <ul> appears on the page. A few modifications are possible:

  • with rvm_css installed, adding real_sheets="1" to the smd_style tag would change the URLs to ‘real’ CSS file paths
  • adding use_default="1" will add the current section’s stylesheet to the list as well. If it happens to be the same as one of the ones in the list already, it will only be used once
  • adding skip="2" will cause the ‘yellow’ and ‘green’ sheets to be static (non-alternate)
  • using promote_recent will change the load order as follows:
    • 0 : load order will always be yellow, green, brown, blue, pink, black
    • 1 : promote the chosen sheet up the list. With use_default="1" skip="2", if the visitor chose ‘pink’ as their theme and refreshed the page, the load order would be: yellow, pink, green, brown, blue, black (yellow and pink would be listed as “static” stylesheets)
    • 2 : force whichever sheet was most recently chosen to be the first loaded. Again, if the visitor chose ‘pink’ as their preferred sheet and refreshed the page, the load order would be pink, yellow, green, brown, blue, black (pink and yellow would be static sheets)

Example 2

To set up the names of the stylesheets you want to include in a <txp:variable /> try this:

<txp:variable name="alt_styles"
     value="girls:Girly theme, boys:Lads only" />
<txp:smd_style sheets="?alt_styles" />

Example 3

Grab the default sheet, the “fixed” sheet (and make them both ‘static’) then look in the custom field labelled alt_styles and the URL variable mytheme for more sheet definitions.

<txp:smd_style sheets="fixed, ?alt_styles, ?mytheme"
     use_default="1" skip="2" />

Note that no distinction is made where to find the variables, they are just checked in order and the first one it finds that matches (if at all) will be used. Thus if you had a custom field labelled alt_styles and someone put site.com/my_page?alt_styles=two,three,four on the URL, you would still have them read from the custom field because it is ‘higher’ in the hierarchy. One caveat is that if the custom field is empty, the next place in the list is checked until all locations are exhausted, though you can ignore empty fields with show_empty="0".

Example 4

For a non-XHTML DTD you may need to drop the trailing / on the <link> elements, so you could do this:

<txp:smd_style sheets="blue, red, green">
 <link rel="{smd_style_rel}" type="text/css"
     media="{smd_style_media}" href="{smd_style_url}"
     title="{smd_style_title}">
</txp:smd_style>

Example 5

The show_empty attribute seems fairly pointless on the surface, but imagine this scenario: you set up a custom field called default_style. When authoring articles, people can insert the name of a stylesheet in there to style the page with. But if they leave it blank you could ensure the page renders with at least some default content by creating a stylesheet called “default_style” (i.e. the same name as the custom field) and using a tag like this:

<txp:smd_style sheets="?default_style"
     show_empty="1" />

If the custom field is empty, the plugin converts the request to <txp:smd_style sheets="default_style" />.

Example 6

Tag parsing.

<txp:smd_style sheets="first, second, third"
     parse_tags="1" />

If one of your stylesheets contained the following:

.filler {
   background:url(<txp:site_url />images/bg.jpg);
   color:red;
}

The TXP tag would be replaced with the contents before being served. This does have a performance penalty as the stylesheet is fetched and parsed, but can be very useful. You may use <txp:php></txp:php> tags in the stylesheet as long as the admin preference to allow page level PHP is enabled. Many thanks to akokskis for some of the code from ako_cssParse that enables this feature.

Changelog

  • 12 Oct 08 | 0.1 | Initial release
  • 18 Oct 08 | 0.2 | Added parse_tags (thanks akokskis / johnstephens) ; added skip_title ; fixed invalid link tags ; real_sheets now work without a form/container

Experimental software

If you’re feeling brave, or fancy trying something new, you can test out some of my beta code. It can be found on the plugin beta page.

Legacy software

If, for some inexplicable reason, you need an old version of a plugin, it can probably be found on the plugin archive page.

Whack-a-Stef

This particular game of whack-a-mole was way too easy