Click here to Skip to main content
15,888,977 members
Articles / Web Development / CSS

CSS Variables

Rate me:
Please Sign up or sign in to vote.
2.83/5 (20 votes)
17 Dec 2006CDDL2 min read 139.3K   449   32   47
Using HTTPHandlers to support dynamic CSS.

Introduction

Users want flexibility in the websites they visit; they want to define what content they see, and how it is shown. Website developers want to give them that ability, but need to balance it against maintainability. Using Cascading Style Sheets (CSS), the developer can specify certain base layouts, or themes, and allow the user to change these at runtime. However, creating separate files for all possibilities is just not reasonable. A better way would be to change certain CSS values at runtime based on settings specified by the user, yet CSS does not have variables that can be evaluated at runtime.

Solution

To get around the problem of not having variables in CSS, one must read the CSS file and replace the given values at runtime. The good news is that in ASP.NET, this is a relatively easy task.

CSS
body
{
   background-color:#BG_COLOR#
}

Generic HTTPHandler

Using Visual Studio 2005, you can easily add a Generic HTTPHandler.

Add New Item

This will create an ashx file and add it to your project. This file implements the IHTTPHandler interface, with its one and only method, ProcessRequest, and includes the WebHandler page directive.

ASP.NET
<%@ WebHandler Language="C#" Class="Handler" %>

The .NET Framework treats these files as HTPPHandlers without the need to register them in the <httpHandlers> section of the web.config file.

ASP.NET
<%@ WebHandler Language="C#" Class="CSSHandler" %>

The code-behind:

C#
using System;
using System.Web;
using System.Configuration;

public class CSSHandler : IHttpHandler 
{
    public void ProcessRequest (HttpContext context) 
    {
        context.Response.ContentType = "text/css";
        
        // Get the file from the query stirng
        string File = context.Request.QueryString["file"];
        
        // Find the actual path
        string Path = context.Server.MapPath(File);
        
        //Limit to only css files
        if(System.IO.Path.GetExtension(Path) != ".css")
           context.Response.End();

        //Make sure file exists
        if(!System.IO.File.Exists(Path))
           context.Response.End();

        // Open the file, read the contents and replace the variables
        using( System.IO.StreamReader css = new System.IO.StreamReader(Path) )
        {
            string CSS = css.ReadToEnd();
            CSS = CSS.Replace("#BG_COLOR#", 
                      ConfigurationManager.AppSettings["BGColor"]);
            context.Response.Write(CSS);
        }
    }
    
    public bool IsReusable 
    {
        get { return false; }
    }
}

As we can see, the ProcessRequest method simply opens the file specified on the query string, reads it, and uses string replace to add the value for the variable that has been specified in the web.config file. Not much to it.

XML
<link rel="Stylesheet" href="CSSHandler.ashx?file=default.css" />

<appSettings>
   <add key="BGColor" value="Red"/>
</appSettings>

Limitations

Using a generic webhandler has a disadvantage in that you must specify the style sheet to parse. This breaks down when using ASP.NET 2.0 Themes, however, because any stylesheet placed in the theme folder will automatically be linked, no need to manually add it to your web pages. Although you can manually add each one, it isn't a very maintainable model.

Better solution

A better solution is to create a custom HTTPHandler and add it to the httpHandlers section of the web.config file.

XML
<httpHandlers>
    <add verb="*" path="*.css" 
       type="CustomHandler.CSSHandler, CustomHandler"/>
</httpHandlers>

The code:

C#
public class CSSHandler : IHttpHandler 
{
#region IHttpHandler Members

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(System.Web.HttpContext context)
    {
        // Get the physical path of the file being processed
        string File = context.Request.PhysicalPath;

        // Open the file, read the contents and replace the variables
        using(System.IO.StreamReader reader = new System.IO.StreamReader(File))
        {
            string CSS = reader.ReadToEnd();
            CSS = CSS.Replace("#BG_COLOR#", 
                      ConfigurationManager.AppSettings["BGColor"]);
            context.Response.Write(CSS);
        }
    }

    #endregion
}

The only difference from the previous example is that the CSS file to parse is obtained from the context.Request.PhysicalPath property. Since the handler is registered for CSS files, it will process any stylesheet file regardless of its location in the web project.

Conclusion

This article has hopefully shown a method that can be used to provide dynamic settings to an otherwise static file and give website users a more positive experience.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)



Comments and Discussions

 
AnswerRe: Not working for me Pin
Not Active24-May-07 15:46
mentorNot Active24-May-07 15:46 
GeneralRe: Not working for me Pin
Saumin25-May-07 4:52
Saumin25-May-07 4:52 
GeneralRe: Not working for me Pin
Not Active25-May-07 5:03
mentorNot Active25-May-07 5:03 
GeneralRe: Not working for me Pin
Saumin29-May-07 5:23
Saumin29-May-07 5:23 
GeneralGreat Work Pin
dbeard10-May-07 8:10
dbeard10-May-07 8:10 
GeneralVariable Source Pin
JoeReynolds1-Apr-07 6:56
JoeReynolds1-Apr-07 6:56 
GeneralRe: Variable Source Pin
Not Active1-Apr-07 13:53
mentorNot Active1-Apr-07 13:53 
GeneralLots of praise [modified] Pin
jokva14-Feb-07 6:58
jokva14-Feb-07 6:58 
Thank you so much for this article, and for taking time to share it!

The reason I would like to underline the above is that the comments you have received are unappreciative, unimaginative, and frankly rude.

When someone sits down to give to the community, it is pretty small of you to try to show off through your comments. If you are so brilliant, I suggest you try writing your own articles. What is wrong with you people?!

I know this is from a while back, but I just came across this today. So, sorry for my late remarks.

- But this is good stuff! It is as simple as it is brilliant, and to me it seems to have a lot of positive implications.

I've been playing around with this for a few hours, and in my opinion you have opened a door to a much better way of managing Themes. It appears this would allow me to dedicate whole sections of my CSS files to specific browser/browser versions (see below).

What I have done so far is to play around with simple whitespace removal. Themes and css friendly controls create a big pile of css files. Even if the size increase from whitespace is marginal, its worth doing when it can be done cheaply. And if I can use a single set of css files on both the development and the production server, I am certainly better off.

A couple of discoveries I would like to share: browsers seem to accept and handle headers on text/css files as well. Hence, you could benefit from adding cache headers. I tried the following headers, and they seem to create the effect I wanted, at least in IE7, FF, and Opera (well, opera goes without saying, as it seems to be caching everything, but..):

Dim reader As System.IO.StreamReader = New System.IO.StreamReader(File)
Dim CSS As String = reader.ReadToEnd()
Using (reader)
'this is not important. this should be regex based
CSS = CSS.Replace(Environment.NewLine, " ")
CSS = CSS.Replace(" ", " ")

End Using
context.Response.Cache.SetExpires(Now.AddDays(10))
context.Response.Cache.SetValidUntilExpires(True)
context.Response.Cache.SetLastModified(Now.ToUniversalTime)
context.Response.Cache.SetMaxAge(New TimeSpan(10, 0, 0, 0))
context.Response.Cache.SetRevalidation(HttpCacheRevalidation.None)
context.Response.Cache.SetCacheability(HttpCacheability.Public) //could be serverandprivate, too.
context.Response.Cache.SetSlidingExpiration(True)
context.Response.BufferOutput = True
context.Response.ContentType = "Text/Css"
context.Response.Write(CSS)

(pardon my vb)

Not sure what would be an ideal configuration of the cache, but I have noted that Firefox seems to go nuts if the ContentType = "Text/Css" is not present.

Looking at the File objects in IE, these headers are added, and the browsers all seem to reuse the cached objects.

Hence, if one can easily add to this varyby browser/version, I suppose this could be a real step towards overcoming all css hacks with asp.net, too?

I imagine one can easily employ regex to handle a css file that uses special comments markup to target specific browsers, e.g.:

/*<Generic>*/
css for all browsers
/*</Generic>*/

/*<ContemporaryBrowsers>*/
css for new browsers
/*</ContemporaryBrowsers>*/

/*<IE6>*/
only for IE6 here.
/*</IE6>*/

etc.

Hope that was constructive. And thank you again!


-- modified at 7:19 Thursday 15th February, 2007
(had to clean it up a little Smile | :) )

Jo



GeneralRe: Lots of praise Pin
Not Active14-Feb-07 8:03
mentorNot Active14-Feb-07 8:03 
GeneralSecurity & Performance Pin
Evyatar Ben-Shitrit18-Dec-06 19:54
Evyatar Ben-Shitrit18-Dec-06 19:54 
GeneralRe: Security & Performance Pin
Not Active19-Dec-06 2:10
mentorNot Active19-Dec-06 2:10 
GeneralDangerous Pin
The .NET Junkie18-Dec-06 7:40
The .NET Junkie18-Dec-06 7:40 
GeneralRe: Dangerous Pin
Not Active18-Dec-06 7:51
mentorNot Active18-Dec-06 7:51 
GeneralRe: Dangerous Pin
The .NET Junkie18-Dec-06 8:31
The .NET Junkie18-Dec-06 8:31 
GeneralRe: Dangerous Pin
Not Active18-Dec-06 8:56
mentorNot Active18-Dec-06 8:56 
GeneralRe: Dangerous Pin
Brian Lowe18-Dec-06 9:43
Brian Lowe18-Dec-06 9:43 
GeneralRe: Dangerous Pin
Not Active18-Dec-06 10:59
mentorNot Active18-Dec-06 10:59 
GeneralRe: Dangerous Pin
evolved19-Dec-06 5:29
evolved19-Dec-06 5:29 
GeneralRe: Dangerous Pin
trooper08149-May-07 16:55
trooper08149-May-07 16:55 
Generalbetter solution Pin
selap18-Dec-06 6:27
selap18-Dec-06 6:27 
GeneralRe: better solution Pin
Not Active18-Dec-06 6:50
mentorNot Active18-Dec-06 6:50 
GeneralRe: better solution Pin
selap20-Dec-06 5:47
selap20-Dec-06 5:47 
QuestionGood concept, but what about performance? Pin
Steven Berkovitz17-Dec-06 8:28
Steven Berkovitz17-Dec-06 8:28 
AnswerRe: Good concept, but what about performance? Pin
Not Active17-Dec-06 10:17
mentorNot Active17-Dec-06 10:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.