CSS Constants

Preface

One feature designers often wished they had with style sheets are constants - the chance to define something once and reuse it over and over in the style sheet document. This article shows some techniques how to achieve that and discusses their pros and cons.

A constant nagging

When reading CSS related forums or mailing lists, you'll sooner or later find someone asking how they can define a "constant" in CSS - something to define once in the CSS document and reuse throughout it.

There are several reasons why there are no constants in CSS:

However, CSS constants could be something very handy indeed.

Possible Uses

CSS constants would make it easier to turn style guides into CSS and allow for smaller style sheet documents without much repetition. You define the colours once and add the constant name every time you want to use it.

/* Company Colours */ 
$blue='#369';
$green='#363';
$lgreen='#cfc';

[]

ul#navigation{
	background:$blue;
	color:#fff;
}
h1{
	border-bottom:1px solid $green;
}

White-labelling and redesign would be a breeze, all you need to change is the constants and all the styles comply with the new colours. No more search and replace sessions and the inevitable setting that escaped them.

Another option would be to define often used styles and keep the document shorter.

$boxstyles='background:#ccc;border-top:1px solid #eee; border-left:1px solid #eee; border-right:1px solid #aaa; border-bottom:1px solid #aaa;padding:.5em;margin:.5em 0';

h1{
	$boxstyles
	color:#000;
}
div#extras div {
	$boxstyles
	font-size:80%;
}

As CSS does not allow for that, we need to find a way around the problem. One is to use what CSS does provide us with.

The CSS standard compliant approach

CSS in itself is something like a constant. By defining the styles in classes and IDs or for the elements themselves, we spare ourselves the excruciating work of adding a lot of visual elements and attributes in the markup. In the days before CSS we had to do the following:

<td valign="top" align="right"><font size="+1"><font color="blue"><b>Foo</b></font></font></td>

CSS allows us to define that as:

td {
vertical-align:top;
text-align:right;
font-size:100%;
color:blue;
font-weight:bold;
} 

These constant settings are applied to each TD element. Another way:

td.special {
vertical-align:top;
text-align:right;
font-size:100%;
color:blue;
font-weight:bold;
} 

The class special is a definition that can be reused for all elements, so to say a constant.

Using Classes

CSS classes could be one approach to mimic real CSS constants. Our box example above could be:

.box {
background:#ccc;
border-top:1px solid #eee; 
border-left:1px solid #eee; 
border-right:1px solid #aaa; 
border-bottom:1px solid #aaa;
padding:.5em;
margin:.5em 0';
}

We can add this class to any element to apply its definitions. It is also perfectly valid to add several classes, separated by spaces, to one element.

<h1 class="box">
<div class="box warning">

What we should avoid doing is to name the classes according to how they look, as it can be pretty confusing to see a <div class="blue bold underline"> that is yellow, of normal weight and without an underline, after a new style guide came along and got applied.

Using ID Selectors and Descendent Selectors

Another option to maintain a certain re-use is to make the settings dependent on an ID on the BODY element. First we define the settings applying to all pages:

body{
	font-family:Arial,Sans-Serif;
	background:#fff;
	color:#333;
}
#nav{
	width:10em;
	margin:0;
	padding:.5em;
}
#nav li{
	list-style-type:none;
}
h1{
	font-size:120%;	
	border-bottom:1px solid #000;		
}

Then we make the colours dependent on the ID of the body:

#home h1{
	border-bottom:1px solid #363;	
	background:#cfc;	
}
#home #nav{
	border:1px solid #363;	
	background:#cfc;	
}
#contact h1{
	border-bottom:1px solid #369;	
	background:#ccf;	
}
#contact #nav{
 	border:1px solid #369;	
	background:#ccf;	
}

The higher specificity ensures that the original settings get overwritten by the "localised" ones.

Depending on which ID we set on the BODY element, the different colour settings get applied, without any need to define classes in the HTML.

While this approach means a lot of repetition in the style sheet, it still - properly commented - provides an easy way to brand different pages differently.

All we need is to add one ID to the BODY of the document - something that could be done dynamically on the server or via a content management system.

Both solutions are dependent on changes in the markup though, the classes approach painfully so. Real CSS constants shouldn't need that, and to make that happen we have to move away from the browser to the server.

Moving server side

There are basically two ways to simulate CSS constants on the server. One is using the server software and the other is to use scripting languages on the server.

Using Server Side Includes (SSI)

Server Software, like Apache and the Microsoft IIS allow for server side includes (SSI). We can use these to simulate CSS constants.

For Apache, we define a new file extension called "sccs" for "Server Cascading Stylesheets" by adding the following to the configuration file of the server:

Options +Includes
AddType text/css .scss
AddHandler server-parsed .scss

Inside one of these new "scss" files we can use the Server Side Includes syntax to set and retrieve our constants:

<!--#set var="blue" value="#369" -->
<!--#set var="green" value="#363" -->
ul#navigation{
	background: <!--#echo var="blue" -->;
	color:#fff;
}
h1{
	border-bottom:1px solid <!--#echo var="green" -->;
}

Many web space providers won't allow users to change the server configuration, though. What most provide are Server Side Scripting Languages, that can also be used to simulate CSS constants.

Using Server Side Scripting Languages

Server side languages, such as PHP, JSP, ASP, ASPX, Cold Fusion, ModPerl, Perl, JSP and so forth, come with the ability to send header information of the current document. Furthermore, they feature all we need - Variables, Constants, Loops, Conditions and Objects.

To tell for example a PHP script to send its output as CSS to the browser, we use the following two lines of code:

header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 

The first line defines the content as CSS, a style sheet. The second sets the expiry date of the document to the current time and a bit, that way the output of the script will still be cached by the browser. Without this line, the CSS would never be cached, which can be a performance problem depending on how big the resulting style sheet document is.

You can create anything with the right header information. Javascript, images, PDFs, Excel Sheets, Flash files, you name it. Header can also help us to trigger the download of files rather than the display of them in the browser.

The only important thing is that the header output has to be the first output of the script. Any output before that - including whitespace - will cause an error.

Content types can only be set once, you cannot put out Javascript and later on CSS (unless you write them out inline) in one script.

Harnessing the power of the header function, we now have the whole specification of our server side language at our disposal. We can define and reuse variables, change them according to parameters sent to the page, automatically set them according to the location on the server and so on.

Our above example in PHP would be:

<?php
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 
/* Company Colours */ 
$blue='#369';
$green='#363';
$lgreen='#cfc';
?>

ul#navigation{
	background:<?php echo $blue;?>;
	color:#fff;
}
h1{
	border-bottom:1px solid <?php echo $green;?>;
}

If the PHP setup allows for shortcut notation we could also use:

<?php
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 
/* Company Colours */ 
$blue='#369';
$green='#363';
$lgreen='#cfc';
?>

ul#navigation{
	background:<?=$blue;?>;
	color:#fff;
}
h1{
	border-bottom:1px solid <?=$green;?>;
}

Another way is to use the print command to output until a certain label is reached; this one does mess with the colour coding of some editors and might be - depending on the size of the CSS - straining the processor:

<?php
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 
/* Company Colours */ 
$blue='#369';
$green='#363';
$lgreen='#cfc';

print <<<ENDCSS
ul#navigation{
	background:$blue;
	color:#fff;
}
h1{
	border-bottom:1px solid $green;
}
ENDCSS;
?>

There is no end to the usefulness of this approach. We can reuse cookie data or grab selector names from a database or a parsed document. We could allow an XML notation for the CSS and turn it into valid CSS on the fly.

An example of how to use parameters sent to the page could be to allow visitors to define a width of the content:

<?php
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 

// grab the parameter w and set the variable w to its value
// if the parameter is not an integer, set w to 740
$w=740;
if(isset($_GET['w']))
{
	$w=preg_match('/(\D)/',$_GET['w'])?$w:$_GET['w'];
}
/* Company Colours */ 
$blue='#369';
$green='#363';
$lgreen='#cfc';
?>
#boundary{
	width:<?php echo $w;?>px;
}

ul#navigation{
	background:<?php echo $blue;?>;
	color:#fff;
}
h1{
	border-bottom:1px solid <?php echo $green;?>;
}

Or we apply different colour schemes according to the section:

<?php
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT"); 
$self=$_SERVER['PHP_SELF'];
/* Colour schemes */ 
if (preg_match('/aboutus/',$self))
{
// About us
	$colour1='#369';
	$colour2='#69c';
	$colour3='#9cf';
} 

else if (preg_match('/clients/',$self))
{
// Clients
	$colour1='#963';
	$colour2='#c96';
	$colour3='#fc9';
}
else if (preg_match('/downloads/',$self))
{
// Downloads
	$colour1='#693';
	$colour2='#9c6';
	$colour3='#cf9';
}
?>
ul#navigation{
	background:<?php echo $colour1;?>;
	color:#fff;
}
h1{
	border-bottom:1px solid <?php echo $colour3;?>;
	color:<?php echo $colour2;?>;
}

Each of these examples requires the maintainer of the site to know about PHP, and may give her the chance to break it or add unsafe code. If we want to ensure that we have something like CSS constants with a less steep learning curve and less power to the maintainer, we can parse a CSS file with PHP.

Parsing CSS with PHP

Parsing a CSS file before it gets sent to the server allows us to come up with a notation for the constants. As an example we will use $constant = 'value'; as the definition and $constant as the implementation.

Our CSS looks something like this:

/* Demo CSS */
/* Constants 

$colour1 = '#999';
$colour2 = '#363';
$colour4 = '#696';
$colour3 = '#cfc';

*/
body{
	text-align:center;
	background:$colour2;
	font-family:"MS Trebuchet", Arial, Sans-Serif;
}
#boundary{
	font-size:90%;
	margin:1em auto;
	text-align:left;
	position:relative;
	width:40em;
	background:$colour2;
}
[]

We apply it to the HTML document via the LINK or the style tag, like any other CSS, the only difference is that we send it to a PHP script for parsing:

<style type="text/css">
	@import 'cssconst.php?c=demo.css';
</style>

The script to parse the CSS and write out the values of the defined constants is rather simple:

<?PHP
header('content-type:text/css');
header("Expires: ".gmdate("D, d M Y H:i:s", (time()+900)) . " GMT");

// grab the c parameter and ensure that it contains .css is no slashes
// this is a safety measure to prevent XSS
$c=$_GET['c'];
if(preg_match('/\//',$c) or !preg_match('/.css/',$c))
{
	die('only local CSS files allowed!');
	exit;
}

// load the content of the CSS file into the variable css, end if the
// file wasn't found.
$css=load($c);
if($css=='')
{
	die('File not Found, sorry!');
	exit;
}
// grab all constants and store them in the array constants
preg_match_all("/\\$(\w+).*=.*\'(.*)\'/",$css,$constants);
for($i=0;$i<sizeof($constants[1]);$i++)
{

// replace all occurrences of the contants with their values
	$css=preg_replace('/\\$'.$constants[1][$i].'/',$constants[2][$i],$css);
}

// delete all constant definitions
$css=preg_replace("/\\#.*=.*?;\s+/s",'',$css);

// print out the style sheet
echo $css;

function load($filelocation)
{
	if (file_exists($filelocation))
	{
		$newfile = fopen($filelocation,"r");
		$file_content = fread($newfile, filesize($filelocation));
		fclose($newfile);
		return $file_content;
	}
}
?>

To see it in action, check the demonstration page with the unparsed style sheet.

Disallowing the script to read other than local files and only CSS files are the basic safety measures we should enforce when using this technology. Allowing any script in any location to be parsed might enable visitors to spy on sections of the server they are not supposed to look into.

For basic operations, this is a slick approach to the problem, however to allow for more complex definitions we might have to amend the regular expressions - for example to allow for multi line definitions or the usage of quotation marks inside a definition.