Introduction

CSS Stylesheets are a major improvement in the World Wide Web, allowing HTML to come back to its roots: a language to structure and publish content on the web, not to apply a visual formatting to that content. Unfortunately HTML and CSS have the same limitations: both are static languages!
From Perl scripts to PHP, a lot of technologies are today available to move HTML to the dynamic age but why not, going even further and do the same with CSS? That's what I did when developing eDContainer (a container for web sites and web applications), having the following objectives in mind:
  1. To provide an external, user-friendly, visual configuration file: the user doesn't have to edit the stylesheet, he may not be familiar with CSS and even if so, properly editing a stylesheet requires some experience.
  2. To not have to repeat general identical values like fonts and colors x times within the stylesheet: the value is defined once in the visual configuration file and inserted automatically by PHP in the different locations within the stylesheet.
  3. To provide a solution to the implementation of skins or themes (similar to Winamp or Windows XP principles): several different visual configurations can be defined and dynamically switching from one to the other is possible.
  4. To be able to change some style behaviors dynamically, depending on some configuration values.
If you're quite familiar with PHP and CSS, reading the above objectives will probably make you already think about some solutions to achieve them. That's the fun about coding, always different and sometimes better ways to obtain the same results! When writing this article, I'm not saying that what is described here is the only way to do it. It's a way and I hope I'll manage to keep you interested: Let's start!

Principles

The main principle of this solution is that all the involved components, including the stylesheet, are PHP components that will be interpreted by the web server before being offered to the user. All the involved components will therefore be named with a .php file extension but the visual configuration one that will use a .inc extension (include PHP file).
Illustrating this article requires the creation of three files:
  1. The CSS stylesheet stylesheet.css.php file that is a normal CSS stylesheet but including some PHP code. For that reason, and although the .css extension is kept, the file is named as a PHP file. For some reason that I don't know yet, this type of filename containing two dots can cause some problems on some operating systems. If so, simply change the filename from stylesheet.css.php to stylesheetcss.php.
  2. The visual configuration visuals.inc file that will contain the visual configuration parameters as an array of key-value pairs (similar to the WINDOWS INI file way). This file is being included in the CSS stylesheet in order to make the visual configuration parameters available to the stylesheet.
  3. The index index.php file that is our test file and will mainly contain HTML code using the styles defined in the stylesheet.
To avoid any confusion, those three files are assumed to be stored in the same directory. The location of this directory is not important as long as it is located somewhere under the root of your web server. Let's move on to the definition of the visuals.inc file.

The Visual Configuration File

This file is meant for the end user. Although being still a text/php file, its layout as well as the way variables are named is taking that into account: being as user-friendly as possible is the key. For that reason, I made the choice to not use variables but multi-dimensional associative arrays that are giving the opportunity to name the different element in a very comprehensive way.
This array is named $VISUALS in capital letters to follow a naming convention I'm using for all "global" arrays. The array is first indexed on an integer value to allow multiple configuration sets to be defined. This is the way the eDreamers tools are made "skin-able", you can jump from one set to another by just changing the id which can be provided by an URL (HTTP Get request) for example.
The parameters that you want to make available via this visual configuration file are up to you. It fully depends on your stylesheet and what is the freedom that you want to give or not give to your end user. In the scope of this article, we will consider the following very simple example:

<?php
 
// Retrieving POST and GET variables
 
if (substr(phpversion(),0,3) != '4.1') { 
  
$_REQUEST array_merge($HTTP_POST_VARS$HTTP_GET_VARS); 
 }
 
// Define which skin you'd like to use.
 
if (isset($_REQUEST['SkinID'])) { 
  
$SkinID $_REQUEST['SkinID']; 
 } else if (!isset(
$SkinID) ) { 
  
$SkinID  '0'
 }
 
 
// Default Skin
 
$VISUALS[0]['page']['font'] = 'Verdana, Tahoma';
 
$VISUALS[0]['page']['fontsize'] = '10';
 
$VISUALS[0]['page']['fontunit'] = 'px';
 
$VISUALS[0]['page']['background']['color'] = '#838383';
?>
The first step is to retrieve the values that might have been passed to the _visuals.inc file by one of the three ways variables can be passed from one PHP script to another:
  1. via the URL using an HTTP GET request (myfile.php?key=value);
  2. using an HTTP POST request;
  3. or including (via the include()/require() function) the visuals.inc file so gaining visibility on the parent script variables.
Since PHP 4.1, the way HTTP POST/GET request variables are handled has also changed (we won't discuss that here, refer to the PHP web site) and that's the reason why the first step in the script is, if the PHP version is not 4.1, creating and initialising a $_REQUEST array (PHP 4.1 compliant behavior) containing the HTTP_POST and GET arrays (used by former versions of PHP). This makes sure the rest of the script can be developed following the latest recommendations of PHP while still working fine on former versions (the test != '4.1' is not fully correct as also returning true for higher versions of PHP).
The next step is making sure that whatever value passed to the visuals.inc file in whatever way (see above) is always taken into account. The only value that we will use is the $SkinID that is referencing the id of the visual set to load. The code first checks if a value is passed via HTTP POST or GET request (using the $_REQUEST initialised sooner), if no checks that the variable is not yet defined by a parent script (assuming visuals.inc might have been included or required by another script) and if none of those scenarios are true, finally give the $SkinID variable a default value equal to 0.
Finally comes the definition of the visuals parameters and for this example I limited their number to four: font, font size, font unit and page background color. I recommend that, when naming the first dimension of the $VISUALS array, you link it to structural elements of your web page. Let's imagine that you define visual parameter for the footer section of your web then declare a $VISUALS[0]['footer'] dimension.
The visuals.inc file is now ready; let's move on to the stylesheet.

The CSS Stylesheet File

The PHP enabled stylesheet is nothing more that a CSS stylesheet with some PHP code in it. What is more interesting is the benefits of that association as, purely from a coding point of view, there's no complexity at all.
As for the visuals.inc file, the first steps are making sure that any variables passed on to the stylesheet.css.php are properly retrieved. This is where we'll encounter one of the first benefits of including PHP code in stylesheet: implement browser based behaviors without having to create multiple stylesheets. This will be discussed with more details in the next sections.
To be able to use the visual parameters defined in the visuals.inc file, this file must be included in the PHP enabled stylesheet. This is done using the require() function, as without this file, the stylesheet would not behave properly. Now can start the real fun of putting some PHP in CSS (I assume you know the basics about CSS). I won't explain anything here; see the next sections to understand the why.

<?php
 
if (substr(phpversion(),0,3) != '4.1') { 
  
$_REQUEST array_merge($HTTP_POST_VARS$HTTP_GET_VARS); 
 }
 if ( isset(
$_REQUEST['browser']) ) { 
  
$browser $_REQUEST['browser']; 
 } else if ( !isset(
$browser) ) { 
  
$browser  'ie5';    // 94% of the browsers on the web
 
}
 require_once(
'visuals.inc');
?>

body, h1, h2, h3, h4, h5, h6, p { 
 font-family: <?php print($VISUALS[$SkinID]['page']['font']); ?>
 font-size: <?php 
  
print($VISUALS[$SkinID]['page']['fontsize']);
  print(
$VISUALS[$SkinID]['page']['fontunit']);
 
?>;
}

body {<?php
 
print('background-color: '.$VISUALS[$SkinID]['page']['background']['color'].';');
?>}

<?php
 
for ($i=1$i<6$i++) {
  print( 
'H'.$i.' { ');
  print(
'font-size: '.($VISUALS[$SkinID]['page']['fontsize']+(6-$i)));
  print (
$VISUALS[$SkinID]['page']['fontunit'].';');
  print(
'font-weight: bold;');
  print (
' }');
 }
?>

The Index File

As for the visuals.inc file, the first steps are making sure that the $browser variable passed on to the index.php are properly retrieved. However this is not the time to discuss this yet.
This whole discussion would have no sense if we were not creating the link to the stylesheet. It seems that the stylesheet is not interpreted when using the following code <style type="text/css" src="stylesheet.css.php"> (your comments are welcomed) so we will include the stylesheet definitions directly in the index file instead:

<?php
<style type="text/css"><?php require('stylesheet.css.php'); ?></style>
?>
The rest of the code is some pure HTML to test the styles.

<?php
 
if (substr(phpversion(),0,3) != '4.1') {
  
$_REQUEST array_merge($HTTP_POST_VARS$HTTP_GET_VARS); 
 }
 if ( isset(
$_REQUEST['browser']) ) {
  
$browser $_REQUEST['browser'];
 } else if ( !isset(
$browser) ) {
  
$browser  'ie5';
 }
?>
<html>
 <head>
  <title>Sample Index File</title>
  <style type="text/css"><?php require('stylesheet.css.php'); ?></style>
 </head>
 <body>
  The current browser is <?php print($browser); ?>
  <h1>Heading 1</h1>
  <p>
   Paragraph 1.1
  </p>
  <h2>Heading 2</h2>
  <p>
   Paragraph 2.1
  </p>
  <h3>Heading 3</h3>
  <p>
   Paragraph 3.1
  </p>
  </body>
</html>
?>

The Real Fun!

Code Generation
This is the most obvious use of PHP inside a CSS stylesheet but not the one with the highest added value. One example is the creation of repetitive style definitions like for the HTML headings, in/decreasing the font size automatically from the font size set in the visual configuration file:

<?php
for ($i=1$i<6$i++) {
 print( 
'H'.$i.' { ');
 print(
'font-size: '.($VISUALS[$SkinID]['page']['fontsize']+(6-$i)));
 print(
$VISUALS[$SkinID]['page']['fontunit'].';');
 print(
'font-weight: bold;');
 print (
' }');
}
?>
The main advantage is for the lazy ones, not having to type the same thing over and over again. More seriously, this can automate the creation of style definitions in various situations and I'll give more examples if requested.

Browser/Platform Based Behaviors

Usually, Javascript is being used in the index file of the web site in order to detect the browser/platform and load a different stylesheet. It happens that the difference between this and this browser/platform specific stylesheet are not so big and being able to adapt a unique stylesheet would ease everything (let's avoid duplication). For example, font size (again) can be a problem depending on the Operating System (OS) of your visitor. Even at the same resolution as on your WINDOWS OS, the text will appear to be smaller on Linux and Mac OS platforms. I don't have the chance to own a MAC OS so the value tested may not be fully correct (extracted from index.php):

<script type="text/javascript" charset="ISO-8859-1">
 var browser = navigator.appName.toLowerCase();
 var platform = navigator.platform.toLowerCase();
 var detect = ; if ( platform.indexOf("mac") != -1 && detect) {
  location.replace("index.php?detect=false&browser=msie6");
 } 
</script>
Warning: Be careful, each browser on each platform reacts differently to those instructions and tests are required to make sure that your Javascript will behave as expected. Even the browser detection scripts on website such as Javascript.com cannot be considered as working correctly in all situations.
An easy way to make the $platform variable to the PHP code is to recall the index file with some parameters. This is almost invisible for the visitor but do not forget to include a $detect variable (initialised to true if not set) to avoid to enter a loop where the index file is replaced and replaced again. As soon as the parameter (here platform) is detected, the $detect variable is set to false.
The variable $platform is now available to the index file and therefore to the stylesheet. You can test it using a switch() { case: ; } block and, in our example, increase the font size by 2 in case a Mac based platform is detected (extracted from stylesheet.css.php):

<?php
body
h1h2h3h4h5h6
 
font-size:<?php 
 
switch($platform) {
  case 
'mac';
   print(
$VISUALS[$SkinID]['page']['fontsize'] + 2;
   break;
  default:
   print(
$VISUALS[$SkinID]['page']['fontsize'];
   break;
  } 
 print( 
$VISUALS[$SkinID]['page']['fontunit'] ); ?>;
 ?>;
}
?>
The value of this increment should depend on the font unit that you use (px, pt, em, etc.). This increment could also be declared as a new visual parameter to be included in your visual configuration file as:

<?php
$VISUALS
[0]['page']['fontsize']['increment']
?>
(extracted from visuals.inc).

Implementing Skins

Remember the visuals.inc file and its $VISUALS indexed multi-dimensional associative array, it's now time to add a new configuration set. This is easily done by copy and pasting the existing set and increasing the skin id by 1(extracted from visuals.inc):

<?php
$VISUALS
[1]['page']['font'] = 'Arial';
$VISUALS[1]['page']['fontsize'] = '12';
$VISUALS[1]['page']['fontunit'] = 'pt';
$VISUALS[1]['page']['background']['color'] = '#838383';
?>
Once you have set new values you can put the following code in the index.php file to switch from one skin to another:

<?php
<a href="index.php?SkinID=<?php $SkinID ? echo '1' : echo '0'; ?>">
?>
Using the browser detection as evoked in the "Browser/Platform Based Behaviors" section, you could also decide to load a totally different skin using a code similar to this (extracted from index.php):

<?php
if ( browser.indexOf("explorer") != -) {
 
location.replace("index.php?SkinID=1);
}
?>

Contextual Behaviors

I call contextual behavior the capacity of a style or class within the stylesheet to depend on an external information that is, in most case, the value of another configuration or visual parameter. The code sample in the "Code Generation" section was demonstrating that kind of behavior by using the default text font size visual parameter to calculate the font size of the Headings in order to obtain a certain consistency between the different font sizes.
This is not the use of contextual behaviors I prefer. Contextual behaviors are a lot more interesting when used to make the stylesheet react on non visual parameters. Again, there can be hundreds of examples and choosing one is quite difficult so I will list some:

Conclusion

Bringing the power of PHP inside the already powerful CSS stylesheets can bring you a lot of new opportunities and as many problems.
The importance of a good design prior to development (or during a prototyping phase) is always essential and always more essential when dealing with technologies as rich as PHP and CSS, and this all the more true when they are combined.
That the reason why making a clear distinction between the structure, the visual and the content related aspects of your web sites or web applications is an absolute must if you want to achieve good results.
All your comments and suggestions are, of course, welcomed.