Using Variables in a CSS File

We've all had to do it: the client wants things a little lighter/darker/bolder/slimmer/whatever and I'm up late at night search-and-replacing RGB values or color hex values throughout my CSS and JavaScript.  Yuck!  If you're a good programmer, you at least used variables in your JavaScript for those times that you needed to do styling.  But there's nothing you can do about CSS.  Or is there?

 People have posted all over the web about ways to use PHP to generate your CSS.  In fact, it's as simple as

<link type="text/css" rel="stylesheet" media="all" href="style.php" />

So what's the big deal?  Caching.  A properly cached website will load much faster than an uncached one.  This is easy to see when using Firefox and Firebug -- just switch to the "Net" tab in Firebug and Ctrl+F5 to force a full reload of your site.  Now do a regular refresh (F5).  Everything that comes back with a "304 Not Modified" status means the server didn't have to transmit that file again, the browser used a local, cached copy instead.  Because of the dynamic nature of most pages written with PHP, the default headers tell browsers not to cache anything created with PHP.  With some tweaking, we can change that:

  $time = filemtime( $file );
  $etag = md5_file( $file );
  $mod = gmdate( 'D, d M Y H:i:s', $time ) . ' GMT';

  /* Check if we can used cached file */
  if(( @strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) == $time ) ||
     ( trim( $_SERVER['HTTP_IF_NONE_MATCH'] ) == $etag )) {
    header( "HTTP/1.1 304 Not Modified");

  /* Write CSS header info */
  header( "Content-type: text/css; charset: UTF-8" );
  header( "Cache-Control: must-revalidate" );
  header( "Last-Modified: $mod" );
  header( "ETag: $etag" );

Simply add this to the top of any PHP-based CSS file and it'll deal with caching based on either the Last-Modified date or the ETag value, in this case, an md5 hash of the file contents.  Better yet, save this as a and require_once it at the beginning of your PHP-based CSS file.

After that, you can write your CSS how you like.  I prefer using heredoc notation.  For example:

$hSpace = "15px";
$vSpace = "10px";
$color = "#123456";

echo <<<END_OF_CSS
/* CSS file starts here */
div {
  margin: $hSpace $vSpace;
  color: $color;
/* And so on... and so on... */

An interesting side-effect of this change has been that I'm much more consistant with my use of colors and spacing.  If I want to add another color to a site, the process of adding another variable forces the question, "do I really need another color?"  Similarly with spacing, because I have standard margins and paddings defined, I'm less likely to guess at how much space to put between elements and more likely to question the need for a different margin or padding amount.

What about the .css extension?

Now, some purists want CSS files to have the extension .css.  Fine...  With a little .htaccess fiddling, we can do that.  In this case, it's best if your PHP-based CSS files are all located in a single directory so you can add an .htaccess file to that directory and not burden your server with processing every .css file on your system as a PHP script.

First check to see if you server runs PHP as FastCGI or not.  Look at the output of phpinfo() and find the "Server API" entry. If it says "FastCGI," guess what...  It's running as FastCGI (this is good).  If so, add this to your .htaccess file:

AddHandler php-cgi .css
Action php-cgi /cgi-bin/php-cgi

If not, add this:

AddHandler application/x-httpd-php .css

where /cgi-bin/php-cgi points to your FastCGI binary (you can find more info at the FastCGI FAQ).

I've only tried this on one relatively small part of my website so it hasn't been thoroughly tested in a Drupal environment yet.  I'll post updates on this page if I find any problems.

UPDATE: OK, I have found one problem with this system.  If you turn on Drupal CSS optimizations, the optimizer gets completely confused by the PHP.  Works like a charm in a non-Drupal setup or with CSS optimizations turned off. Seems like there would be a quick reg-ex fix for this.  I'll have to look into this more...

UPDATE #2: A solution to the above problem with CSS optimizations is to use drupal_add_css() to add the parsed CSS files and set the preprocess parameter to false.

Add new comment

The content of this field is kept private and will not be shown publicly.