Dynamic CSS A.K.A. CSS Variables

Cascading Style Sheets are an amazing tool, having transformed Web Design as an industry and the Web as a communications medium. But in all of its glory (and it is glorious), CSS has some flaws. The most frustrating for me is its static nature. I want to be able to reuse values (CSS variables if you will), I want to make use of browser detection instead of CSS hacks, or IE’s proprietary conditional comments and I want to use logic to serve up specific rules in certain situations. Enter PHP (or your server-side langugage of choice), which will provide us a wealth of new options for working with our CSS, including the ability to adapt to our end-users’ needs.

Hello…. JavaScript Can Do That Already!

Yeah, client-side scripting can handle most of these requirements, but I don’t want to rely on a technology for my presentation that can be disabled by the user or their corporate IT department and frankly the JS support of mobile devices is poor at best. Enter server-side languages, which cover all of these bases without impacting the program or device rendering the site.

What I’ll Cover

The Setup

This article assumes that you have a Web server at your disposal that is running one of the aforementioned scripting languages and that you have a basic understanding of scripting in that language and know a bit about CSS. For the purpose of this write up I’ll be using PHP, but I have implemented versions of this system on production sites running JSP, and there’s no reason the same couldn’t be done with Ruby on Rails, Python, ASP or any other server-side solution.

This system can be as detailed or relaxed as you’d like, so I’m going to keep this write-up relatively simple with a few interesting bits thrown in. If it proves to be an interesting subject I’ll write a follow-up showing advanced usage. Let’s hit it!

Serving a Dynamic (PHP) File as CSS

In your editor of choice create a new PHP file. I’m calling mine styles.php. Paste this line at the very top of the document:


	<?php header('Content-type: text/css'); ?>

This tells the server that the page should be sent to the browser as a CSS document, so the file won’t need the .css extension. All of our work will be done in this bad boy. So, on to the next step.

Useful Variables and Practices

So, we have a PHP file that has a serious personality disorder, thinking that it’s a style sheet. What the hell are we going to do with it? Well, we’re going to set a few key variables that will make life much easier. Specifically, we’ll set variables for common measurements (columns, containers and the space between) and some colors from the site’s palette:



$gutter           = '10px';   /* Used to separate visual blocks */
$pageWidth        = '1000px'; /* Width of the Web page */
$pageWidthPadded  = '980px';  /* $pageWidth minus padding ($gutter x 2) */

$col1             = '800px';  /* Width of the main content area */
$col1Padded       = '780px';  /* $col1 minus padding ($gutter x 2) */
$col2             = '180px';  /* Width of the sidebar area */
$col2Padded       = '160px';  /* $col2 minus padding ($gutter x 2) */

$lightGray        = '#e5e5e5';
$charcoal         = '#464646';
$orange           = '#f60';
$darkBlue         = '#0059b2';

Dynamic CSS Illustration The width variables will prove useful in creating a site as they guarantee consistent presentation from page to page and template to template. This model can easily be extended for multiple column widths, so if you want to subdivide Column 1, you only have to set it up once, creating the variables ($col1A, $col1B etc.) and setting their values to the appropriate widths. For you folks who love them grids, this should make life pretty easy.

Setting up color variables simplifies the initial site build-out and future revisions. When you return to the style sheet six months from now, you’ll notice $darkBlue is much easier to remember than #0059b2.

So, now that we have width and colors, it’s simple to use these new variables in the style sheet, all you need to do is use PHP’s echo shortcut (<?= 'print this' ?>):


#Wrapper {
	background-color: <?= $lightGray ?>;
	border 1px dotted <?= $darkBlue ?>;
	margin: <?= $gutter ?>;
	width: <?= $pageWidth ?>;
}

Advanced: I’ve begun to separate the measurement value (px, em, pt) from the actual numerical value so I can harness the power of PHP to do my math for me. That’ll be the subject of a future write-up.

Implementing Browser Detection to Avoid CSS Hacks

I’ve used a couple of different packages in the past; at the moment I use BrowsCap, which covers these needs nicely, but use whatever fits your needs best. Place this code at the top of styles.php, after the PHP header line:


require_once('Browscap/Browscap.php');

// Create a new Browscap object (loads or creates the cache)
$bc = new Browscap('Browscap/cache/');

// Get information about the current browser's user agent
$current_browser = $bc->getBrowser(null,true);
$browser_platform	= $current_browser['Platform'];
$browser_name		= $current_browser['Browser'];
$browser_ver_major	= $current_browser['MajorVer'];
$browser_ver_minor	= $current_browser['MinorVer'];
$browser_ver_full	= $current_browser['Version'];

$is_ie6 = false;
$is_ie7 = false;
$is_safari = false;
$is_ff = false;

if ($browser_name == 'IE' && $browser_ver_major == 6) {
  $is_ie6 = true;
} elseif ($browser_name == 'IE' && $browser_ver_major == 7) {
  $is_ie7 = true;
} elseif ($browser_name == 'Firefox') {
  $is_ff = true;
} elseif ($browser_name == 'Safari') {
	$is_safari = true;
}

So we now have a few variables that will allow us to quickly determine whether the user’s browser is IE 6, IE 7, Firefox (any version) or Safari (any version). You can extend this model to fit your specific audience and needs.

Putting it All Together

Sweet, now we can rock the styles all dynamic-like! Here are a couple of examples for you to nibble on:


body {
<? if($browser_platform == 'MacOSX') { ?>
  font: .85em/1.4 normal "Lucida Grande", Verdana, sans-serif;
<? } else { ?>
  font: .75em/1.4 normal "Lucida Grande", Verdana, sans-serif;
<? } ?>

That block outputs a larger font size for folks who are on a Mac compared to folks on PCs or other devices.
The next one modifies the value of a top margin, serving up 5px for IE 6, and zero for all other browsers.


#NavWrapper1  {
  display: block;
  height: 18px;
  <? if ($is_ie6) { ?>
    margin-top: 0;
  <? } else { ?>
    margin-top: 5px;
  <? } ?>
  margin: <?= $gutter ?>;
  width: <?= $pageWidthPadded ?>;
}

Advanced: This method can be used to detect alternate browsers as well. So, you could detect a mobile browser and serve up a separate style sheet. Or if you detect a screen reader you could serve up a style sheet with proper aural rules defined.

That’s it, we’re rolling with dynamic CSS, harnessing the power of both PHP and Cascading Style Sheets. There is a lot more that can be done, but I hope this opened a few avenues for experimentation on your future projects.

In Moderation: Too Much Considered Harmful

It would be really easy to overuse this method. Do not try to replace proper use of grouping and the cascade with PHP. You won’t gain anything but frustration and larger style sheets. Take the time to think through the variables that you are going to create to make sure they make sense and are not simply recreating existing CSS functionality.

Acknowledgments

I am by no means the only person to think of this, there are many others who have covered the topic on their sites, but I felt that there were a few components missing, so here we are. I hope I added to your toolbox. It is also very important to note that there are some great folks with whom I’ve worked who have expanded on the concept and helped me flesh it out to be a viable and reusable tool in the Real World ™, most notably the inestimable Leesa of Red Velvet Cafe.

Your Turn

I look forward to your feedback and questions, so please leave a comment.

Comments

  1. violinista says

    Great! This is real proof of Keep It Simple & Sweet (KISS) concept, which I follow! Like it, thanks!

  2. says

    Alex, great post. I don’t like to use dynamic CSS for my main style sheet, but I’ve used it to great effect in secondary style sheets to serve up a different color scheme and ‘browser fixing’. Also, I find that PHP is great to serve up images as well. Instead of pointing to a static image, I’ll point to a PHP script sitting inside a directory.

    One gripe: kinda seems silly to use $lightGray as a dynamic var.

  3. says

    Thanks for the feedback violinista and Ryan!

    Ryan – I agree, that is an odd name for a variable, and I actually debated using names that describe the physical representation of their contents, which is a major pet peeve of mine in CSS (Leesa can attest to my ranting on the topic). In this case I decided to use those names as I thought they made it a bit more understandable to folks who were reading the post and hadn’t encountered some of these concepts for the first time. The next post won’t follow that practice, shifting towards a bit more advanced audience.

    Oh – and obviously I still need to follow up and actually bust out that next post. Luckily that’s what I’m going to do once I’m done writing this…

  4. says

    Hello Alex,

    I’ve been very interested reading you’re article on Dynamic CSS. I’m currently trying to complete a study site on which I’m trying to experiment with different techniques. The site is meant for publishing the stuff I’m supposed to do for a course in PHP programming I’m just now trying to finish. On this site, which mainly targets my fellow students, I’d like to show the picture of the student that has logged on to it. I’m planning on adding the capability of uploading their picture themselves, though. I show the picture via CSS background. I have been looking for a way to make the url dynamic, which brought me to your site. I’ve been experimenting with your technique. It works like a charm for putting things like colour in place through a PHP vriable, but I don’t seem to be able to use a PHP variable to replace the url for the picture with the url I hold in a session variable and which is queried from the MySql database that holds the site users. I started out with:
    background: #90EE90 url(../Pics/ran.jpg) no-repeat;
    When I try this however:
    background: #90EE90 url() no-repeat;
    I do get the green background colour, but no picture.
    Replacing the #90EE90 by a PHP variable gives me the green background colour allright, but whatever substitution I try for the url, I don’t seem to be able to get it to work. It will only show the picture if I hardcode it with url([picture path & name]). Would you happen to know if there is a way to make this url-bit dynamic with PHP too? Or should I not bother and put the picture in my XHTML-page in stead of in the CSS, which I would consider a defeat? Love to hear from you! Cheers

    Ran

  5. says

    Hi Ran, thanks for commenting on the article!

    In regards to your project, it is definitely possible. You mention that you are pulling the path from their session variable which then matches to an ID in the database, are you doing this in the style sheet PHP orin the PHP for the main page? If you aren’t doing it in the style sheet, how are you passing the photo path to the style sheet?

    Also, I noticed in the code you included in your comment that url() is empty, which could be due to WordPress stripping code from your comment for security reasons. IF that is the case, please let me know if I am just repeating what you’ve alredy done in the example below.

    Here’s a bit of pseudo-code as to how this should work:

    
    <?php
    /*
     Your code that grabs the session ID
     and matches it against the DB, resulting
     in a path to the proper photo.
    
      The resulting path is stored in the
      variable $userPhotoPath
    */
    ?>
    
    // Within the Dynamic CSS
    #YourPictureID {
    	background: #90EE90 url(<?= $userPhotoPath ?>) no-repeat 0 0;
    }
    

    If that doesn’t work, can you confirm that you are indeed successfully capturing the user’s session information and that your code is finding a match in the database, which in turn has a photo assigned to the matched record?

    I hope this helps! Let me know if you have any other questions, or are still running into problems, and I’ll help if I can.

  6. says

    Hi Alex,

    thanks for your swift reply! As I was pondering the issue at hand this morning in bed, after just being woken up by my 2 year old daughter, I suddenly realized that the problem probably must have to do with the $_SERVER variable. I must have forgotten to put the session_start() call in the PHP-CSS, if you know what I mean! Indeed, after checking later today, I had forgotten to put that in. Thing is, though, I started yesterday with putting everything on-line, which means fixing all sorts of reference and database problems, so I wasn’t really able to quickly check if it would fix the problem. I have just now had a successful first log on (still some trouble with references in the main page, though) and: voila! The picture is indeed placed through the PHP-CSS! I was just about to let you know, and found your reply already. Great! Sorry for the waste of your time, though. I should have gone over it just a bit more. I was a bit frustrated, yesterday, that I couldn’t get it to work proper. The empty url() indeed is the result of WordPress stripping the code. I would like to invite you to my site now, of course. Remember though that it is in dutch (I did wonder if I would make it multi-lingual, but decided that would have to be for another time!), that it is meant as a testbed to try all sorts of techniques, that it is there to support my PHP-course and – last but not least – that I may be an experienced developer, but that I only started out on web development last October, when I took a course in HTML, in CSS and as from November a course in PHP. So I’m still trying to get to grips with a lot of concepts and methods that are alien to me. I must say: most frustrating so far has been the difficulty with which to debug these scripts (XHTML, Javascript, CSS and PHP, not to mention the phpMyAdmin for the MySql database!), especially since I have enthusiastically wrought them all together. I do feel I’m finally getting the hang of it, though, and coming to the same conclusion as you have all by my self, does make me hopeful! I do apologize again for wasting your time like this and promise to try harder still before I’ll ask for help again! If you do decide to drop me a visit, you’ll be asked to log on . After a successful log-on you’ll reach a page with on the right half sports two buttons. The first is active and has the label “gastenboek”. This means “guestbook” and clicking it will bring up a tinyMCE editor in which you can leave a message. After bringing up the editor, the second button becomes active too. It says “opslaan”, which stands for “save”. If you want me to be able to read your comment, you should click it. Would be fun, I think, and make me proud too! Cheers Alex, and have fun!

    Ran

  7. says

    That’s great news Ran! We all get frustrated at times, and often all it takes to find the answer is explaining the problem to someone else. It sounds like you are well on your way to developing these very useful skills – stick to it, even with the annoyances, Web development is a lot of fun and very rewarding.

    I’ll stop by your site soon. Cheers!

  8. rula says

    Hi Alex,

    very fine and handy article about dynamic css.
    I will use it in jsp’s and have a problem with it.
    When I use *.jsp (anstead of *.css) for my dynamic style-sheet it works, and the jsp-varaibles will be evaluated.

    e.g.

    But if I use *.css (anstead of *.jsp) it not works.
    I will retain the css-extension for my dynamic style-sheet. How can I tell the application-server (e.g.tomcat) to handle this *.css as *.jsp. Needs it somthing like this:

    css
    text/jsp

    or what have I to do solveing this problem?

    regards, rula

  9. rula says

    Hi Alex,

    oh sorry, html-tags won’t be displayed.
    With somthing like this, I mean:
    Have I set the mime-type of css to another value in the web.xml, that tomcat handle the *.css-file as *.jsp?

    regards, rula

  10. says

    Hi Rula, thanks for commenting!

    I don’t know how to set tomcat to treat all CSS like JSP, but I would recommend against it, as you may have style sheets that don’t include any dynamic variables, but by setting CSS to be parsed by Tomcat, you’re adding extra overhead when the file is loaded. In the past, I’ve named my files style.css.jsp or similar to make it easy for me to see which ones are CSS and which are not.

    I don’t have any answers if you feel the trade-off is worth it, as it has been quite a while since I worked with JSP, and I typically had server admins to make changes like that.

    Best of luck, and if you find the answer, please feel free to leave it here in the comments for future searchers.

  11. wizard20740 says

    Typically the dynamic part of stylesheets is only a small/miniscule portion of the overall css content. So if we could isolate the dynamic portion inside a single separate file and make it accessible for use across all the remaining static stylesheets (especially in websites which use multiple css), we could avoid unnecessary load on the server.

    Not sure whether this is currently possible. Understand though that a proposal to use CSS @Variables at rules, for implementing this, is under consideration.

  12. says

    Interesting point wizard, but if I understand you correctly, the server still has to process the variables, and the difference in size between a single stylesheet with everything included versus breaking it up into separate file isn’t likely to make a difference. But, by breaking it up into separate stylesheets, you are actually adding overhead as the browser has to pull each separate stylesheet, queuing them up with all of the other files, which could slow down the entire page load. There’s a definite bonus to having everything in a single file when looking at network performance and browser rendering.

    I’ve been tinkering with a different angle of late – building everything dynamically and then copying the generated file for use, so the server doesn’t have to do any processing except for the single time after I make a change to the dynamic style sheet. I have some more experimenting to do on that though.

    I’ve paid some attention to the @Variables rules, which would be great if they are implemented properly, but sadly it will be a long time before we could reliably use them on sites. Until then conversations like this one are great.

  13. I. Chna says

    why does Alex get to keep on breathing – seriously, I’m totally serial.

    I’ve seen all his dvd’s, slagging of the Leaders of the US civilisation, very young.
    He never talks about CHINA, and their Red Army, their 5000 year old civilisatins. Holding, if interpritated the birth of the illuminati accross Europe and beyond. Sure, Norad, less than 100 years, the great wall of china – thousands of years.

    the push to far, your dreams are
    China in your hands.

    the Chinese tiger is out of the woods, and too many years ago to do anything, they are sending their unwanted babies to the US.

    bye, and stop terrorising folks, killing is killing, this show is the media of Japanese torture, dreams vs reality.

    bye,
    etherability.

    PS
    you cannot tell people stuff, enlightened or not, like black and white, sure chinese don’t hit in either place.
    Why, in lush Africa, has the land been turned to sand, something real hot happened, when they returnrd from ‘land of the rising sun’, how the world looked 5000 years.

    turning Japanese, I think I’m turning Japanese, I F’in know so. I’m also bigger than you think so. Born of greed not a living seed.

  14. says

    Howdy I. China. You’ve found the wrong Alex Jones, try infowars.com – he’s likely the one you’re talking about.

    Cheers

  15. I. Chna says

    Really, how does Alex Jones from infowars, who gas publically infiltrated the Bohemian Grove, keep on breathing,

    could you “?”

    maybe I have the correct Alex Jones, and like you say, he’s the wrong one?, or on the Goebells agenda.

    Please come back with you bollox on the first comment in this box.

    thank you for you readings of my English language?, not complicated or sopisticated enough for real communications,
    I’ll not re-connect. for these writings of this type of known by the few knowledge, sorry y’all

  16. I. Chna says

    ever seen the dancing at the Bohemien Grove with 5 people, prancing around feeding from the joy, in a pentonic style.
    ‘Pentagon’, site at last minute re-located to be built on what’s called?

    If America want’s it, mat she’ll get it, and love it, like Beauty and the monster.

    China in your hans, that army corp you have, the big red 1 , is as big as the Red Army uno bollox yu must feed your desire for them, then be enslved by their products, like your enslavement by all your chinese products, ALL.

    Funny Funny Funny, not fun fun blah blah, funeral.

    blowing smoke up the devils ass, funny. bomb bomb

  17. gavin says

    Wonderful article, and your blog looks great.

    Am I seeing that you name all your css files with a .php extension? If you want to keep you .css extension and use php tags in them, try this:

    /css/.htaccess:
    RewriteEngine on
    RewriteRule \.(.*)$ dynamic_css.php [L]
    
    /css/dynamic_css.php:
    if ($file_name = basename($_SERVER['REQUEST_URI'])) {
        header('Content-type: text/css');
        eval('?>'.file_get_contents($file_name));
    }   

    Of course it means adding 2 new files to your css folder. But it makes dynamic-css files transparent to the rest of your application.

  18. says

    Great idea Gavin! I didn’t want to treat all CSS as PHP, adding needless overhead, but I hadn’t thought about a local .htaccess file.

    Very smart, thanks for sharing it!

  19. gavin says

    thanks. By the way, you can remove CSS comments by adding this code to the script I posted before:

    $file_contents = preg_replace(‘!/\*[^*]*\*+([^/][^*]*\*+)*/!’, ”, $file_contents); // removes comments from css

  20. micharo says

    Hi,

    I try read a custom field (background color) from a page and use that value in a dynamic cssStyle.php.

    But if I add a template function in the cssStyle.php like get_post() it does not work.
    I assume the problem is that the cssStyle.php is called in the header and template functions are not available.

    Any help appreciated!

  21. micharo says

    I got the template functions to work, but still something is not doing right.

    post_parent){//if the current post is a page and has a parent, then:

    $parent_id = $post->post_parent; // assign the id of the parent
    $parent_post = get_post($parent_id); //get the parent post

    // if current page is in the sub sub level or deeper nested
    while($parent_post->post_parent){ // if the parent post has a parent, then
    $parent_id = $parent_post->post_parent;
    $parent_post = get_post($parent_id);
    }

    }else{
    $parent_id = $post->ID; //if it doesn't have a parent, it must be a parent, so its own ID gets stored as $parent.
    }

    $key = "bg_color";
    $single = true;
    $post_id = $parent_id;
    $color_value .= get_post_meta($post_id, $key, $single);

    return $color_value;
    }
    $color = get_color_value();

    // declare the output of the file as CSS
    header('Content-type: text/css');

    ?>
    #headerBg {
    background-color: ;
    }

    Help appreciated!

  22. micharo says

    Sorry, here is the full code

    post_parent){//if the current post is a page and has a parent, then:

    $parent_id = $post->post_parent; // assign the id of the parent
    $parent_post = get_post($parent_id); //get the parent post

    // if current page is in the sub sub level or deeper nested
    while($parent_post->post_parent){ // if the parent post has a parent, then
    $parent_id = $parent_post->post_parent;
    $parent_post = get_post($parent_id);
    }

    }else{
    $parent_id = $post->ID; //if it doesn't have a parent, it must be a parent, so its own ID gets stored as $parent.
    }

    $key = "bg_color";
    $single = true;
    $post_id = $parent_id;
    $color_value .= get_post_meta($post_id, $key, $single);

    return $color_value;
    }
    $color = get_color_value();

    // declare the output of the file as CSS
    header('Content-type: text/css');

    ?>
    #headerBg {
    background-color: ;
    }

  23. says

    Hi Micharo, can you provide a little more information what isn’t working right?

    Also, it looks like some of the code you entered here was stripped by WordPress for security. Please make sure that you escape all less than and greater than signs (&lt; and &gt;) to ensure the code stays whole.

  24. micharo says

    thanks so much for writing back!

    Here is a little bit more info:

    What I want to do:
    I have website with a few main pages and sub pages and sub sub pages. Each area (each main page is an area with some sub pages) has a different color background. In other words a main page and the children (sub pages) have the same background, but each area is different. The color value of the area’s background is stored in a custom field in each main page.

    So I wrote a style_dynamic.php with the following blocks in it.
    - first I require wp-blog-header.php
    - then with a function I get the color value from the custom field of the main page or if it is a child from the top parent (main page)
    - then I declare this php file as a css header(‘Content-type: text/css’);
    - then I write the css style with the variable

    The problem:
    It seems the color value is retrieved from the custom field but somehow not read by the css.

    &lt?php
    require(‘../../../wp-blog-header.php’);

    function get_color_value(){
    global $post;
    if(is_page() && $post-&gtpost_parent){//if the current post is a page and has a parent, then:

    $parent_id = $post-&gtpost_parent; // assign the id of the parent
    $parent_post = get_post($parent_id); //get the parent post

    // if current page is in the sub sub level or deeper nested
    while($parent_post-&gtpost_parent){ // if the parent post has a parent, then
    $parent_id = $parent_post-&gtpost_parent;
    $parent_post = get_post($parent_id);
    }

    }else{
    $parent_id = $post->ID; //if it doesn’t have a parent, it must be a parent, so its own ID gets stored as $parent.
    }

    $key = “bg_color”;
    $single = true;
    $post_id = $parent_id;
    $color_value .= get_post_meta($post_id, $key, $single);

    return $color_value;
    }
    $color = get_color_value();

    // declare the output of the file as CSS
    header(‘Content-type: text/css’);

    ?&gt
    #headerBg {
    background-color: &lt?=$color?>
    }

Trackbacks

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>