Static and dynamic CSS combined

Some of you have probably heard “dynamic CSS”. It originates from a need to serve different CSS at different times, to different people, or on different pages. You do this by first generating the CSS on the server and then sending it off to the users requesting it. There are problems though, since the CSS gets regenerated each time you lose the advantage of caching. You could send HTTP headers so that the file gets cached anyway, but then it’s no longer dynamic right? This article presents a way to both have dynamic CSS that changes on each request, and make sure the user loads it from cache when it can.

How dynamic CSS works

Sometimes you want to send a different set of CSS rules depending on what page you are on. An example could be if your site was module based. Depending on what page you surf to you might get a calendar, some list of latest news or a small box showing the weather in, say, Stockholm.

The functionallity is not too hard to build with some server side language, but what about the CSS? Is it really necessary to load the calendar’s CSS on the news page, where the calendar is hidden? The same thing applies for each of the modules you have on your site; that list of news has certain CSS rules tied to it that isn’t needed on the gallery page. You get the point. So how do we solve this? We could do it my generating it on the fly (I’m using PHP here, but any other language would work):

PHP source:

<?php
// This line makes sure the browser handles
// the file as CSS and not PHP.
header('Content-Type: text/css');

if ($module['calendar'] == true) {
   echo '
      .calendar { width: 300px; }
      .calendar table { background: pink;}
      ...
   '
;
}
if ($module['newslist'] == true) {
   echo '
      .newslist { list-style: none; }
      .newslist li { display: inline; }
      ...
   '
;
}
[and so on every new module added]
?>

Generated file:

.calendar { width: 300px; }
.calendar table { background: pink; }
...
.newslist { list-style: none; }
.newslist li { display: inline; }
...

You save this file as “dynamic_stylesheet.php” and link it like you would if it was a static CSS file. Everything works fine and all the dynamic CSS makes the file sent to the user quite small. Both you and your users are all happy. The site starts growing.

Problems with dynamic CSS

After a while you start getting reports of the site getting slow. After some research you find that it’s in fact the dynamic CSS that does it. The file has gotten quite big and regenerating it each time is a lot of work. So you read up on caching and find that all that’s needed is a few lines at the top of your dynamic CSS file:

header('Cache-control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');

This makes the browser load the file from cache for one hour instead of requesting it again. Problem solved! Right? Well, this means that you won’t get a regenerated file if you visit a new page on the site. The browser will load the style from cache instead and your users will get an unstyled calendar (or any other module) for an hour. Pretty annoying if you ask me.

Say we solve the problem above and get the page to only get cached on a per page basis. There’s still something that feels like a bit of a waste: we require the user to download the entire page again if just one of the modules changes. Say the user moves from the news list to checking out a certain news item. They will probably look pretty much the same, it’s just that the news list’s CSS won’t be needed on the news item page. We would need some way of only telling the browser to only cache parts of the files and make the loading of those parts dynamic.

@import to the rescue

Caching only fragments of your style is certainly doable in a lot of ways. The easiest I have found is using CSS’s own @import command. You simply change the example above to:

<?php
// This line makes sure the browser handles
// the file as CSS and not PHP.
header('Content-Type: text/css');

if ($module['calendar'] == true) {
   echo '
      @import "calendar.css";
   '
;
}
else if ($module['newslist'] == true) {
   echo '
      @import "newslist.css";
   '
;
}
[and so on every new module added]
?>

Generated file:

@import "calendar.css";
@import "newslist.css";

This makes the individual CSS files get cached by the browser (just like directly linking a CSS file would) while still allowing each page to dynamically add the style blocks it needs. A simple solution to a hard problem, just the kind of solutions I like best. What do you think?

23 responses to “Static and dynamic CSS combined

  1. I’m not sure since I havent tested this at all, but would only echoing one line (@import) really be that much faster than echoing out all rules to the css-file, or am I missing something here?

  2. @Fredrik Wärnsberg: compare echoing out 1000 lines of CSS each time versus echoing out 20 lines where each line references a static CSS file.

    For small sites this wouldn\’t matter, for bigger I think it would.

  3. I guess it would be even easier to use style tag in document header instead (containing generated @import references). This way we can save one webserver request.

  4. @Fredrik: The point is to allow the browser to cache as much css as possible but at the same time only load the css that’s needed. If you were to output all of the css onto the page, or into a single file then you run into caching or updating problems (pick the worse of two evils). Whereas outputting a list of @imports, each referencing a cachable file, solves both of these problems and only adds a small amount of extra load.

  5. Pingback: davidbisset.com » Static and dynamic CSS combined
  6. how about using the link tag (alternate stylesheets) to import the different stylesheets

  7. @Johan: that works for a few “modules” but as soon as you get over 5 diffent it starts to feel strange. Feels a little better handling it in the CSS file altogether in my opinion.

  8. How about combining both:

    imported stylesheets and linked stylesheets. Eg:
    linked stylesheet module 1:
    import CSS module 1.1
    import CSS module 1.2

  9. Pingback: Lillicotch.com » Blog Archive » Static and dynamic CSS combined
  10. Pingback: Web Clippings (Geekcraft) 2/17/2007 » Continuous Learning
  11. I have a php script for family history site which I am rebuilding. I have 4 families (grandparents). The script has a template with css. I am looking to have each family pages a different color (switched) in the background via PHP. Each family has the php code ending in tree=01 or tree=02 etc, could you give me advice how to do this please?
    many thanks,
    Dave

  12. Pingback: Static and dynamic CSS combined at 1222north
Comments are closed.