Data-Driven CSS Style Sheets Using ColdFusion

Posted May 5, 2009 at 9:52 AM by Ben Nadel

Tags: ColdFusion

I was talking to someone earlier about applications that have dynamic, data-driven style sheets so I thought I would just touch on one of the ways that I have accomplished this in the past. Normally, when we have a data-driven style sheet, the data-driven aspect is not the entirety of the style sheet, but rather the look-and-feel portions of it. This often includes things like font family, font size, link color, logo image, background color, etc. - generally, things that let a given application have a "branded" look based on client or subscription or some other variable.

Because the data-driven aspects only contain look-and-feel information, I create my page layout and default styles in some sort of generic style sheet (or collection of style sheets). Then, I link to the dynamic style sheet afterwards so that it can override the default styles of the site. There's a number of ways to do this; you could link to a static style sheet that was previously generated:

  • <link href="./css/vendor-#vendorID#.css" ... />

... or, you can write the overriding styles directly to the current page request:

  • <style type="text/css">
  • <cfoutput>#GetCSSTheme( vendorID )#</cfoutput>
  • </style>

... or, you can link directly to a ColdFusion file that generates the style sheet at run time:

  • <link href="./css/theme.cfm?vendor_id=#vendorID#" ... />

Notice that we can pass in meta data to the ColdFusion style sheet URL to help it determine which theme to render. This is the methodology that I am going to demo below:

 
 
 
 
 
 
 
 
 
 

First, let's take a look at the demo page. As you will see in the following code, the demo page links to two style sheets: my generic one and my themed one (which is a ColdFusion file):

  • <!---
  • Param the URL theme. This will determine the look and
  • feel of our site by linked to a dynamic style sheet.
  • --->
  • <cfparam name="URL.theme" type="numeric" default="1" />
  •  
  • <cfoutput>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Database Driven CSS Demo</title>
  •  
  • <!-- Include the generic CSS. -->
  • <link rel="stylesheet" type="text/css" href="generic.css"></link>
  •  
  • <!--
  • Include data-drive CSS. Notice that this LINK tag
  • links to a ColdFusion file, not to a standard .CSS
  • file. This is how we can generate it dynamically.
  • -->
  • <link
  • rel="stylesheet"
  • type="text/css"
  • href="override.cfm?theme=#URL.theme#">
  • </link>
  • </head>
  • <body>
  •  
  • <h1>
  • Database Driven CSS Demo
  • </h1>
  •  
  • <p>
  • This is a demonstration to show how you can have a
  • static style sheet that is then overridden, in part,
  • by a database driven style sheet that exhibits
  • different look-and-feel style properties.
  • </p>
  •  
  • <ul>
  • <li>
  • There is once generic style sheet which is static.
  • </li>
  • <li>
  • Then, there is a style sheet that links to a
  • <strong>ColdFusion file</strong> that generates
  • the look-and-feel styles <em>dynamically</em>.
  • </li>
  • </ul>
  •  
  • <p>
  • Check out the different looks:
  • </p>
  •  
  • <ol>
  • <li>
  • <a href="./?theme=1">Theme One</a>
  • </li>
  • <li>
  • <a href="./?theme=2">Theme Two</a>
  • </li>
  • <li>
  • <a href="./?theme=3">Theme Three</a>
  • </li>
  • </ol>
  •  
  • </body>
  • </html>
  •  
  • </cfoutput>

For demonstration purposes, I am passing a Theme ID through the URL so that we can easily switch between the themes. Normally, this would be some sort of SESSION-based variable that gets set when a user logs into an application. You can either pass this variable through the LINK HREF, or you can simply reference it in the target CFM page.

On the server side, this ColdFusion page then takes the given Theme ID and generates a styles sheet to stream back to the client:

  • <!---
  • Param the URL theme. This will determine the look and
  • feel of the style sheet to generate.
  • --->
  • <cfparam name="URL.theme" type="numeric" default="1" />
  •  
  •  
  • <!--- Tell the client (browser) that this a style sheet. --->
  • <cfcontent type="text/css" />
  •  
  •  
  • <!--- Figure out which style sheet to generate. --->
  • <cfswitch expression="#URL.theme#">
  • <cfcase value="1">
  •  
  • <!--- The default theme. No override. --->
  •  
  • </cfcase>
  • <cfcase value="2">
  •  
  • body {
  • font-family: arial ;
  • }
  •  
  • strong {
  • background-color: gold ;
  • }
  •  
  • em {
  • color: #FF0000 ;
  • }
  •  
  • ul {
  • list-style-type: circle ;
  • }
  •  
  • ol {
  • list-style-type: upper-roman ;
  • }
  •  
  • </cfcase>
  • <cfcase value="3">
  •  
  • body {
  • background-color: #000033 ;
  • color: #FFFFFF ;
  • font-family: monospace ;
  • font-size: 100% ;
  • }
  •  
  • strong {
  • background-color: #FFFFFF ;
  • color: #333333 ;
  • }
  •  
  • em {
  • border-bottom: 1px dotted #FFFFFF ;
  • }
  •  
  • a {
  • color: gold ;
  • }
  •  
  • </cfcase>
  • </cfswitch>

In this demonstration, I am conditionally display CSS rules; this ColdFusion page could, however, be pulling data from a database or even other static files. The method of CSS generation depends on your needs, the traffic of the site, and other performance concerns.

So anyway, that is the basic outline of how I create data-driven style sheets in my applications that require themeing (usually vendor-based). The hardest part is coming up with the right set of CSS rules that will make up the themeing style sheet; you want to allow for good themeing, but not so much that you have to worry about customizing a million different CSS rules.



Reader Comments

May 5, 2009 at 10:03 AM // reply »
92 Comments

Ben...

It's worth noting that using either the 2md or 3rd methods listed above will prevent the browser from caching your CSS. This means that ColdFusion will be required to generated the CSS each time a page containing that code is requested.

In the 2nd example, this reduces SEO to some degree as the style block is inline on the page.

At Dealerskins, we used to take the 3rd approach, but finally moved away from that, instead generating unique CSS files for each of our websites, each time changes are made in our admin.

We created a method in a CFC which contained the full CSS block, along with any conditionals. When the styles for a site are changed, we save the changes to the database, call the method, and rewrite the CSS.


May 5, 2009 at 10:04 AM // reply »
20 Comments

I've done something similar with JavaScript includes before. It wasn't for themes, but used the same methodology. It worked really well for me. :)


May 5, 2009 at 10:13 AM // reply »
11,238 Comments

@Andy,

Those are some really good points. CSS caching can be great and a curse at the same time, depending on what you are trying to do :)

As far as performance goes, obviously, generating a static style sheet is going to be the best as we are just going to be serving up non-ColdFusion files at that point. However, an additional CFM execution can still be quite efficient if you are working off of in-memory data rather than hitting the database each time.

The larger the "theme" CSS, the more likely I am to go with a flat CSS file generated at "update time" as you are referring to with your CFC.

@Adam,

Yeah, definitely, the same technique can be applied to dynamic JS file compilations.


May 5, 2009 at 10:49 AM // reply »
7 Comments

I'd probably do a session var which points to the appropriate style sheet, that way it can be cached (as mentioned above).

This technique is still very useful though - especially for accessibility - being able to have a high contrast style sheet, or a large print version style sheet it a great thing to be able to give users!


Bob
May 5, 2009 at 11:09 AM // reply »
1 Comments

@Andy - can you provide some facts to back up your "this reduces SEO to some degree as the style block is inline on the page" claim?

I've heard this said before, but have always chalked it up to urban legend (just like using tables reduce SEO value) because while everyone seems to say it, no one can seem to provide evidence to back it up.


May 5, 2009 at 11:12 AM // reply »
11,238 Comments

@Bob,

What I think Andy is referring to is simply the fact that you have more "non-content" stuff at the top of the page. If you consider that part of SEO is putting your most relevant information towards the top of the markup, the more inline CSS you have in your HEAD tag, the farther down your "relevant" content is pushed.

Whether or not this actually matters (ie. does the search engine automatically ignore things in the HEAD tag?), I am not sure.


Dev
May 5, 2009 at 11:48 AM // reply »
1 Comments

Hey Ben,

Thanks for writing all this up. :D

All tho the methods you used to swap the css are much more robust then what I was using.

<link href="/css/#SwapName#/main.css" rel="stylesheet" type="text/css" />

And this method can be good for those quick buttons that say, change this site from red to blue to yellow, etc.

The same problem remains, that your re-writing the css structure-per instance of a theme/profile/site. What happens when you have more then 3 small themes? maybe 2000 full website themes, and you're forced to change or update you mark-up.

That won or can't scale when you're the one managing all of the css themes, for each new site created with the app. When I add or change the mark-up, I don't wanna be forced, or force my users to modify what they have going. (I'm trying to think both users that are and aren't css designers)

For you to have a really strong mark-up that allows for updating and adding to the mark-up as the app grows. I can't go and add #newCssID to everyone's css file, it would take for ever. I've done it a few times, and I'm only at 4 or 5 sites right now, lol.

There has to be a way to create one main Css structure. Think Object-Oriented, Polymorphic, Normalized Css.

:D


May 5, 2009 at 7:13 PM // reply »
10 Comments

I like it Ben. I have a new Nursery website project we have just started which will use Morning, Noon and Night shots in different style sheets throughout the day.

On a medium to large size style sheet you would be thinking maybe up to 30kb in size.

Will be good to test the load times each time the css changes.

Leigh


May 5, 2009 at 7:42 PM // reply »
47 Comments

@Leigh,

Just a thought, maybe you should consider putting the elements that change in a separate style so that you're swapping out only the changes during the different times of the day. I'm not a css expert by any means, but on a project that we worked on a couple years ago we did something different with the times of the day the theme would change. Our css/html guy created a separate file for the changes and we swapped it out dynamically, we also did cache the files so that they are not recreated.


May 5, 2009 at 7:47 PM // reply »
10 Comments

@Hatem

Yes i'll test a couple of ways. Cheers for the advice.


May 6, 2009 at 8:22 AM // reply »
11,238 Comments

@Devlin,

I worked on a site several years ago that was basically a single code base with hundreds of "clients" that had their own CSS / images for the site. From what I remember, the way they did it was that the CSS look and feel attributes (color, font, border colors) were stored in a database - one record per client. This was administered via some admin where you could organize client branding. Every time branding was updated, a CSS file was generated and saved to file in the given client directory.

It scaled quite nicely because you only work with look / feel which means the primary style sheet can be updated, no problems. The trick was to generate a CSS file each time you update a given client.

@Leigh,

I second Hatem's suggestions. If you had like:

styles_morning.css
styles_night.css

You could simply but some logic in the HEAD to include one based on time.

Also, sometimes, if the CSS changes are small enough for the different overrides, I simply compress them (remote white space) and include them directly in the HEAD tag in a new STYLE tag. If its small enough, it will load faster than a subsequent call to the browser (in my experience).


May 6, 2009 at 9:17 PM // reply »
5 Comments

I've only done this with PHP but I assume it's possible with CF... why not set up a dynamic page for the stylesheet... this can go get all the other stylesheets (reset, layout, typography etc) then append them with styles from a database, compress them (eg remove unneeded white space, shorted colours from #aabbcc to #abc etc), even gzip them and then output them to the browser after sending appropriate caching headers.

If you really wanted you could process all the styles and remove any that were overridden.

You can even use the link '/dynamicstyles.cfm?3456788987'... eg a 'last modified' timestamp on the end or something which will prevent it from being cached by the browser or ISP.

A similar php version here... http://rakaz.nl/item/make_your_pages_load_faster_by_combining_and_compressing_javascript_and_css_files

By the way, as I type, this box keeps getting longer... every character makes it grow, it's now easily 50x the height of all the text I've typed.


May 7, 2009 at 8:11 AM // reply »
11,238 Comments

@Rick,

What browser are you using? I've been told that happens with really old versions of FireFox, but even when I download portable versions of FireFox, I cannot duplicate the box growing issue?

Yeah, we can definitely combine the styles sheets. My only concern with appending the "custom" CSS to that is that if you have a bunch of clients with customized CSS, you would have to burn a copy of each compiled version for them. That's not a real problem, so long as you have the architecture for it.


May 7, 2009 at 5:46 PM // reply »
5 Comments

Oh good point, I was thinking one set of backend code for separate front end addresses with their own cache dirs... not everyone accessing the same app off the same domain.

I'm running Google Chrome 2.0.177.1


May 8, 2009 at 7:46 AM // reply »
19 Comments

Good Ben, Here is another what we can do:

create a style.cfm file and we can use the theme to load the contents of theme in the css as:

.body {
color: <cfoutput>#coloe#</cfoutput>;
}

we can use the <cfcontent on top as:

<cfcontent type="text/css">
define all the styles down undr

then in main file we can use the style.cfm as:

<link rel="stylesheet" href="style.cfm">

this way dynamic css will be loaded. we can any number of cfswitch and cfcase statements in style.cfm to make as many themes as we can and load from admin panel.


May 8, 2009 at 8:21 AM // reply »
11,238 Comments

@Rick,

Hmm, odd. I've tested this in Chrome and cannot duplicate the issue. I'll keep debugging, thanks.


May 18, 2009 at 7:39 AM // reply »
14 Comments

A search engine usually only checks a page up to a certain number of bytes. Therefore, if you have a lot going on at the top of your page that isn't actually important content, then it could be blocking real content from getting looked at.

This being said however, I think that the number of bytes is still very large and will be able to satisfy most of your needs.

SEO concerns are therefore a little over-estimated.

Mikey.


Jun 24, 2009 at 5:08 PM // reply »
1 Comments

thankss you very good


Jul 5, 2011 at 2:26 AM // reply »
7 Comments

Slightly off topic, but just another note to say how good your website is Ben. It's just so well designed, and your coding examples are fantastic.
Thanks!


Jul 6, 2011 at 9:29 AM // reply »
11,238 Comments

@Peter,

Off topic or not, I truly appreciate that :D Thanks!


Aug 29, 2011 at 2:44 PM // reply »
10 Comments

It has been ten years since I created my last CFM dynamic stylesheet. I am glad I found your tutorial here. The new ideas helped out! Thanks again.


Aug 29, 2011 at 4:30 PM // reply »
369 Comments

I like this post, and will read it more in-depth later. This is something I have been looking for for awhile...something similar to this. Thanks, @Evik James, for commenting on it and bringing it to the top of the recently commented list. I appreciate that. I'm busy writing some code now for our site that needs to go live soon, but once I get the chance, I'll be able to search for this and read over it in a little more detail. Thanks!!! (I have so much trouble sometimes with css...this might help)


Mar 29, 2013 at 8:45 AM // reply »
1 Comments

I had included CSS file in my cfml code as:
<head>
<link rel="stylesheet" type="text/css" href="test.css" />
</head>
but the effect of this test.css is not getting reflected in WebPage when i call it from different section in the same program..

guide me ...



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2013 at 7:46 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
No luck. At least I have uncovered the cause, URLScan 3.1. Here is what I see in the IIS log when a file is over 30mb. 2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8. ... read »
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
May 21, 2013 at 11:51 AM
Ask Ben: Parsing Very Large XML Documents In ColdFusion
Looking at my first ever XML document that I have to parse and put into MS SQL 2000 with CF8. I get it to list the desired Field name, many times over, and have a long list of this field name displa ... read »
May 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
May 20, 2013 at 4:29 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, That's if you're trying to reference a specific row. In this case, we're trying to reference the entire query column as one cohesive value. So, you are correct that if you wanted to output a ... read »
May 20, 2013 at 4:24 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I thought when you used array notation to reference queries you always had to have the row or it would throw a similar error as well? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools