LessCss for CFML Developers

LessCss is a nifty extension to the core CSS language to support variables, calculated values, inclusion-by-reference, nesting, and some other goodies.  Basically the idea is to simplify and reduce duplication in large stylesheets.  Here's a simple example of a variable and a computed value:

@color: #fdd;
#header {
  background-color: @color;
  color: @color / 2;
}

That translates into the following CSS:

#header {
  background-color: #ffdddd;
  color: #7f6e6e;
}

Unfortunately, browsers don't understand LESS, they only understand CSS, so you have to compile it.  Even worse, LESS is implemented as a Ruby gem and/or Rails plugin, which makes it annoyingly complex to deal with.

Fortunately, JRuby and CFGroovy2 will happily smooth things over, and let you do the translation on demand.  For development, this maintains the CTRL-S/ALT-TAB/F5 workflow that we all know and love.  For production, the compilation would be better done during your release creation process, but for sites without a pile of traffic, doing it dynamically is probably no big deal.  You might also want a rewrite engine (like mod_rewrite) to make your URLs prettier or offload the existence checks, but I've not addressed that here.

First, your HTML needs to be changes to have LINK tags like this (this is why the rewrite solution is ideal):

<link rel="stylesheet" href="lesscss.cfm?p=style.less" type="text/css" />

Then you need lesscss.cfm, of course:

<cfimport prefix="g" taglib="cfgroovy2" />
<cfparam name="url.p" />
<cfset thisDir = getDirectoryFromPath(getCurrentTemplatePath()) />
<g:script>
  src = new File(variables.thisDir + url.p)
  dest = new File(src.parentFile, src.name.replaceAll(/\.le?ss/, ".css"))
  variables.regenerate = ! dest.exists() || src.lastModified() >= dest.lastModified()
  variables.srcPath = src.canonicalPath
  variables.destPath = dest.canonicalPath
</g:script>
<cfif regenerate>
  <g:script lang="ruby">
    require 'less'
    $variables["css"] = Less::Engine.new(File.new($variables["srcPath"])).to_css
  </g:script>
  <cffile action="write"
    file="#destPath#"
    output="#css#" />
</cfif>
<cfcontent type="text/css" file="#destPath#" />

Before that'll run, you'll need CFGroovy2 installed (and possibly update the CFIMPORT tag to point at the right location).  You'll also need Groovy, JRuby, the JRuby libs, and Less installed into your /WEB-INF/lib folder.  CFGroovy2 can bootstrap it's own Groovy, but since you have to drop JAR for the JRuby and Less pieces, it's easier to just do the same for Groovy.  Here's the JARs:

With that, restart your server, write yourself a Less file (or grab the sample at the top), and fire 'er up!

15 responses to “LessCss for CFML Developers”

  1. Rostislav

    LESS is really cool. I did something similar in Java and it's probably applicable to ColdFusion too: http://www.asual.com/lesscss/. I still haven't announced it officially because I continue making improvements and probably will add YUI Compressor support.

    One tricky part with such implementation is that if you use @import in the CSS file then such a regeneration check won't be enough.

  2. Sebastiaan

    That's a lot of work for something as simple as a CSS. It would be better to restructure the HTML and CSS or make a CSS per category: font, colour, structure, etc.

  3. Brian FitzGerald

    I, for one, am really pumped to see LessCss coming closer to the CFML world. It looks awesome! I do agree with Sebastian, however, that the amount of installation work does seem disproportionate to the payoff. Maybe one day we can get this built into CF? One can dream, can't he?

    Brian

  4. Henry Ho

    I was talking with my college who specialized in doing front-end. We come to a conclusion that most of what LessCss does can be done by CFML just as well, just a Little bit more verbose? :)

    ##header {
    background-color: #FormatBaseN(color, 16)#;
    color: #FormatBaseN(color / 2, 16)#;
    }

    Maybe it'll be a good idea to write a custom tag to do inline LessCss! :)

    @color: #fdd;
    #header {
    background-color: @color;
    color: @color / 2;
    }

  5. Henry Ho

    oh… the tags are stripped off.
    Just imagine there were <cfoutput> tag on the first example.

    And there were <less:css> on the last example.

  6. Henry Ho

    and… add this to the 1st example… sorry.

    <cfset color = InputBaseN("fdd",16)>

  7. Henry Ho

    Oops, I guess I misunderstood the div 2 operation in LessCss.

    Maybe use a simple <cfcache> tag to eliminate penalty of CF processing on every stylesheet request?

    Nesting is hard, yes, but my colleague and I don't like it very much.

    Mixins can be achieved with &ltcfsaveoutput>.

    CFML is a hammer. :)

  8. Using Less CSS with ColdFusion « The Web Applications Blog

    [...] CSS was originally implemented in Ruby, and Barney Boisvert has already blogged about using the Ruby implementation in ColdFusion in the past.  However the author of Less CSS is now working on a JavaScript version, less.js. This [...]

  9. Jusuf Darmawan

    I just downloaded the jruby and I got only jruby.jar and no profile.jar. Is profile.jar is not necessary now?

    I downloaded jruby (jruby-bin-1.6.8 and jruby-bin-1.7.4) there is no profile.jar on both of them.

    Thanks.

  10. Jusuf Darmawan

    I finally found in jruby-bin-1.4. However I got an error like this:
    org.jruby.embed.jsr223.JRubyContext

    The error occurred in C:/Users/Jusuf Darmawan/Documents/GitHub/mycf/cfgroovy/script.cfm: line 62
    Called from C:/Users/Jusuf Darmawan/Documents/GitHub/mycf/cfgroovy/script.cfm: line 105
    Called from C:/Users/Jusuf Darmawan/Documents/GitHub/mycf/lesscss.cfm: line 12
    60 : detail="Groovy scripts must have at least one expression in them. Forgetting to use <cfoutput> around your <g:script> tag when <cfsetting enableCfOutputOnly=""true"" /> is a common cause of this problem." />
    61 :
    62 :
    63 :
    64 :

    Can you help me with this. I drop all of those jar in the WEB-INF/lib and cfusion/lib but I got this. My cfgroovy is working fine.

    Thanks.

Leave a Reply

You must escape tags within your comment. Use &lt; for < and &gt; for >.