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:
- 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.
- 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.
- 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.
- 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:
- 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.
- 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.
- 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:
- via the URL using an HTTP GET request (
myfile.php?key=value);
- using an HTTP POST request;
- 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, h1, h2, h3, h4, h5, h6, p {
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") != -1 ) {
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:
- Adapt borders of an HTML table depending on the page layout: That's the
main use I make of contextual behaviors in eDContainer. In eDContainer, the page layout can be easily customised by the
web designer via a configuration file and, for example, two horizontal tables
of the page can be joined (with no margin or spacing) or a blank space left
between them (like a line break). If both tables have a 1px border, having
them joined will have the common border displayed as a 2px border. To correct
that, I implemented a simple test in the stylesheet that remove one of the
borders if this layout configuration parameter is set to 'joined'.
- Adapt font size based on the length of a string: Let's imagine that the
main title of your website or any other text is displayed from a PHP variable
that you define in a configuration file or in your index file. This text is
displayed in a specific area of your page and this area is limited in width.
The longer this text is, the smaller the font size should be (still without
certain boundaries) to be able to display it properly. PHP can easily be used
in the stylesheet to determine the number of characters of this string and you
can set the font size based on it.
- Anything that you could think of ...
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.