Dynamic Dot Less Css With Asp.net MVC 2

I have been having a look at the best way to theme a asp.net mvc website in the last few weeks. I heard about dot less css late last year but hadn’t had time to integrate it into a project until now.

Integrating dot less css is pretty easy by default, as shown on its home page however I wanted something that could be configured by application users. We want to be able to specify the link html tag like the following.

<link href="/public/css/sites.less" rel="stylesheet" type="text/css" />

There are few steps involved in the solution I have come up with so bare with me as I go through them. Firstly we’ll start with the web.config:

        <compilation>
            <buildProviders>
                <add extension=".lessx" type="System.Web.Compilation.PageBuildProvider" />
            </buildProviders>
        </compilation>

Insert the builderProviders block inside the compilation section. The compilation section will be under the system.web section. This will enable us to use code blocks in our lessx files.

Secondly, is the routing.

        //Special Route for css so that images are relative to it
        routes.MapRoute("css",
                           "public/css/{filename}.less",
                           new { controller = "Css", action = "Index” },
                           new[] { "eLearning.Controllers" });

This specifies that any url matching /public/css/{filename}.less should be routed to the CssController passing the filename as the parameter to the Index method.

    public class CssController : BaseController
    {
        [OutputCache(Duration = 10, VaryByParam = "")]
        public ActionResult Index(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                throw new ArgumentException("A filename must be supplied");

            ViewData["baseColor"] = "#aabbcc";

            var less = RenderViewToString(filename + ".lessx", null);
            var css = LessCss.Generate(less, true);
            return Content(css, "text/css");
        }
    }

    public static class LessCss
    {
        public static string Generate(string less, bool minify)
        {
            var lessEngine = new EngineFactory();
            lessEngine.Configuration.MinifyOutput = minify;
            var output = lessEngine.GetEngine().TransformToCss(less, null);
            return output;
        }
    }

This is the css controller class. It receives the filename as the input, finds the corresponding .lessx file and returns the file with appropriate view data replaced. Is then takes the less formatted string and runs it through the dot less parser to return a css string. This is then returned to the browser with the appropriate content-type. One method I have not shown is the RenderViewToString() and how the .lessx file is located. This method is located on the base controller class that the css controller inherits from. I have also turned Caching on so that it will cache the result for 10seconds.

        protected string RenderViewToString(string viewName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = ControllerContext.RouteData.GetRequiredString("action");

            ViewData.Model = model;

            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, null);
                ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }


This method finds the .lessx file and returns the result as a string. Now usually the view engine will take controller name and method name and by convention look in the /Views/css/index.aspx file. It doesn’t quite make sense to put it in the views folder so I have overridden the default web forms view engine so that they can be placed in the /Public/Css folder. This can be configured to your liking (convention).

    public class CustomViewEngine : WebFormViewEngine
    {
        public CustomViewEngine()
        {
            var locs = new List<string>(base.ViewLocationFormats);
            locs.Add("~/Public/{1}/{0}"); //My personal choice
            locs.Add("~/Views/{1}/{0}");  //An alternative choice
            base.ViewLocationFormats = locs.ToArray();
        }
    }

Once your Custom View Engine has been overridden, you will need to add it to the ViewEngines.Engines collection in the application start method in the global.asax.cs.

        ViewEngines.Engines.Clear();
        ViewEngines.Engines.Add(new CustomViewEngine());

This will then allow us to write a *.lessx file like the following; you can also pass a strongly typed model to the .lessx file if you so wish in the same way you would to a regular aspx view.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

@basecolor: <%= ViewData["baseColor"] %>;

body {
    background: @basecolor;
}

Which will be rendered to the browser as:

body {
    background: #aabbcc;
}

And that’s it. I’m still working on the finer details but I am liking how it works at the moment. If you have any thoughts or suggestions, please leave a comment.

Adam

11 Responses to Dynamic Dot Less Css With Asp.net MVC 2

  1. [...] This post was mentioned on Twitter by Larry King, Adam Schroder. Adam Schroder said: Just blogged about dynamic #dotlesscss using asp.net mvc 2. Let me know what you think? http://bit.ly/bX2sSy [...]

  2. tgmdbm says:

    Nice.

    To be more in keeping with MVC this would be better implemented as an ActionResult and an extension method on Controller.

    return Less(filename, model);

    also be aware that you can pass variables to the less parser by including them on the query string. you might not need to run it through the whole WebForms engine

    <link href="/public/css/sites.less?basecolor=#aabbcc" rel="stylesheet" type="text/css" />

  3. [...] here: Dynamic Dot Less Css With Asp.net MVC 2 | Schotime.net This entry was posted on Thursday, July 1st, 2010 at 2:33 pm and is filed under Uncategorized. [...]

  4. Schotime says:

    You are indeed correct about the extension method. That is a better way to do it.

    I did see the variables you can pass into the query string. If I was to use that I think I would still run it through a controller as its easy to gather the resources needed to pass to the style sheet in the controller rather than in the master page using a action filter or something.

    Is there a way to pass a list of key value pairs to the parser to be processed with the less file?

  5. [...] to VoteDynamic Dot Less Css With Asp.net MVC 2 | Schotime.net (7/1/2010)Thursday, July 01, 2010 from SchotimeI have been having a look at the best way to theme a asp.net [...]

  6. Nathan says:

    Nice walkthrough and a clever approach, but like some of the other commenters I also think that involving the ViewEngine may be an unnecessary step. Returning a custom ActionResult is more in-line with the intended model in MVC.

  7. This is brillant mate!

  8. Szymon Sasin says:

    Hi,

    instea creatning custom ViewEngine why not use existing facility?


    public static class LessCssHelper
    {
    static LessCssHelper()
    {
    _fileFormats = new[] {
    "~/Content/Stylesheets/{0}.less",
    "~/Stylesheets/{0}.less"
    };
    }
    public static string Generate(string fileName, bool minify)
    {
    var lessEngine = new EngineFactory();
    lessEngine.Configuration.MinifyOutput = minify;
    var output =
    lessEngine
    .GetEngine()
    .TransformToCss(GetFile(fileName), null);
    return output;
    }

    private static string GetFile(string fileName)
    {
    var result = string.Empty;
    var pathProvider = HostingEnvironment.VirtualPathProvider;

    foreach (var format in _fileFormats)
    {
    var nameWithExtension = String.Format(format, fileName);
    if (pathProvider.FileExists(nameWithExtension))
    {
    var file = pathProvider.GetFile(nameWithExtension);
    using (var stream = new StreamReader(file.Open()))
    {
    result += stream.ReadToEnd();
    }
    break;
    }
    }

    return result;
    }

    private static string[] _fileFormats;
    }

    All you have to do is to pass a file name requested.

    Second thing is to keep a MC style ( so without .less extension)

    Regards,
    Szymon

  9. [...] Über diese Bibliothek können wir auch vom Code heraus die Transformation starten. Wie das genau aussehen kann wird hier recht gut beschrieben (auch wenn es schon etwas älter ist) : Dynamic Dot Less Css With Asp.net MVC 2 [...]

  10. [...] On this library we are able to start the transformation from the code. How this could look like you will see if you read this nice description (even if it’s a little bit old) Dynamic Dot Less CSS With Asp.net MVC 2. [...]

  11. [...] On this library we are able to start the transformation from the code. How this could look like you will see if you read this nice description (even if it’s a little bit old) Dynamic Dot Less CSS With Asp.net MVC 2. [...]

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>