Data-Driven CSS Style Sheets Using 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.
Want to use code from this post? Check out the license.
Reader 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.
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. :)
@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.
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!
@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.
@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.
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
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
@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.
@Hatem
Yes i'll test a couple of ways. Cheers for the advice.
@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).
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.
@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.
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
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.
@Rick,
Hmm, odd. I've tested this in Chrome and cannot duplicate the issue. I'll keep debugging, thanks.
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.
thankss you very good
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!
@Peter,
Off topic or not, I truly appreciate that :D Thanks!
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.
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)
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 ...