49 Comments

There have been some interesting developments recently in the field of CSS pre-processors. CSS pre-processors work around the very limited syntax of CSS by allowing you to create your stylesheets using a more powerful, expressive syntax which then gets turned into plain CSS for consumption by all the main browsers. A couple of ones that I've come across are SASS and LESS. LESS is the one I've felt most comfortable with because it's simply an augmentation of the CSS which allows you to incrementally introduce its syntax into your stylesheets.

For example LESS supports variables:

@brand_color: #4D926F;
#header {
   color: @brand_color;
}
h2 {
   color: @brand_color;
}

nested rules:

#header {
  color: red;
  a {
    font-weight: bold;
  }
}

...mixins, operations on colors and size, etc. Check out the  lesscss.org homepage for more.

LESS was originally a Ruby gem, but now also has implementations on ASP.NET (dotlesscss.org) and PHP. LESS has now (inevitably) been re-implemented in JavaScript by Alexis Sellier (github.com/cloudhead/less.js). As well as supporting pre-processing *.less files into *.css via the command-line, it can also be actually run live, directly in your browser!

<link rel="stylesheet/less" href="stylesheets/main.less" type="text/css" />
<script src="script/less-1.0.38.min.js"></script>

I'm not sure I'm quite ready to take the plunge running it live, in-browser on sites just yet, although maybe I shouldn't be according to Dmitry Fadeyev:

Wouldn't live processing lag? Not really. Two reasons for this. One: Less.js has been written from the ground up for great performance, so even browsers with poor JavaScript implementation should still run it very well (Less.js is about 40 times faster than the Ruby implementation—so suffice to say it's a lot faster.). Two: On modern browsers that support HTML5, Less.js will cache the generated CSS on local storage, making subsequent page loads as fast as pure CSS.

Running on Windows

The command-line compiler lessc is targeted to *nix-based platforms and requires node.js which, the last time I checked, doesn't run on Windows (UPDATE: it runs fine under Cygwin, there's a nice simple standalone version here). But Windows has had the ability to run JavaScript directly since the 1990's using Windows Script Host. So I took the latest distribution copy of less.js from GitHub at https://github.com/cloudhead/less.js/tree/master/dist/ and included it via a *.wsf file and stubbed/faked/implemented a few things like window, document and XMLHttpRequest that less.js assumes will be present (which are presumably provided by node.js?), and added a bit of command-line argument handling.

Download

Browse the code and download from GitHubhttps://github.com/duncansmart/less.js-windows and feel free to fork it and send me pull requests!

Usage

To use it, invoke lessc.wsf via cscript.exe like so:

cscript //nologo lessc.wsf input.less [output.css] [-compress] 

I've also added a less.cmd shim which will simplify it to just:

lessc input.less [output.css] [-compress] 

If you don't specify an output.css the result is written to standard output. The -compress option minifies the output. I'll look into implementing the other command-line arguments supported by lessc in due course.

I've added a couple of test files, so you can see if it's working like so:

C:\code\lessc-wsh>lessc test.less
/* Variables */
#header {
  color: #4d926f;
}
h2 {
  color: #4d926f;
}
/* Mixins */
#header {
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
}
...

Future

I may look into hooking this into Visual Studio's T4 support so that the *.css files are generated as soon as you save the *.less files in Visual Studio. (UPDATE: See Phil Haack's T4CSS that does this, instead using  the .NET port of LESS. Thanks to David De Sloovere for pointing this out.)

Update

Mark Lagendijk has created a nice little Windows GUI for the script. Check it out at http://winless.org.

Follow @duncansmart on Twitter

Comments

Comment by Matt De Leon

This is great! Haven't tested it yet but I will when I get a chance...I have been trying to Less.js to work with my browser add-on but it fails on Firefox (due to security issues I think). But compiling in advance solves the problem. Bing bang boom!

Comment by Duncan Smart

Yes, I'll update the post with a link to Haacked's post.

Comment by Ryan Ludwig

Hi Duncan,

Thanks so much for putting this together. As one of those designers who's spent the last few hours trying complex installations on Windows, this was a breeze :)

I have run into 2 hickups though.
Pathing: Whenever I have a path to a webfont or a background image, I'm getting some extra ././'s
expected result: src: url('webfonts/woff/myfont.woff') format('woff')
compiled result: src: url('.\.\webfonts/woff/myfont.woff') format('woff')

Hover states: for some reason, I'm getting an extra space on my :hover elements.
expected result: a:hover {blah blah}
compiled result: a :hover {blah blah}

It's not too bad to do a few find and replaces before upload, but if you have any insights on how to fix this it'd be awesome.

Thanks again!

Comment by Joseph McCullough

If you're using :hover, you need to have an ampersand before it. The ampersand concatenates with the parent. So a{ &:hover{} } will output a:hover , but without the ampersand you just get a space.

Comment by Duncan Smart

Ryan, regarding your webfont path issue: I'll take a look at that. Could you post a bot more of your style declaration so I can try to repro?

Comment by Duncan Smart

Appears to be a bug in the version of the JScript engine being used in WScript. If you use the online less compiler with IE in Compatibility View you get the same result as my script. Maybe some difference in the Regex engine?

Comment by JAGH

I'm sorry for the bother, again, but I have another issue (this is file2.less):

/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}

The part of "display/**/: block; /*sorry for IE5*/" causes a error on compilation when compiled alone, I think it's correct, but when I have ( this is allCss.less):

@import "file1";
@import "file2";

It fails silently, don't generates allCss.css, assuming that file1.css has correct sintax.

JAGH
Comment by Rasmus Fløe

Just what I needed :)

I've now got a nice setup running with something called "File Watcher Simple" I found on sourceforge and your package. Makes my work so much simpler!

Thank you.

Rasmus Fløe
Comment by Mac Duy Hai

I was having a similar issue with backslashes, so I stuck in
css = css.replace(/\\/g, '/');
before outputting the code.

Mac Duy Hai
Comment by Ruth

Absolutly excellent!

I have been trialling both compass(with sass and haml) and less for the last 2 days. Less seemed considerably easier to use, all bar the issue of serving up css files via javascript. That was the single biggest headache. Yes, for Mac users there is a compiler, but no simple solution for windows users - this file seems to be the answer.

Now.. if someone could create an adobe air app.. that would be the ultimate LOL

Thanks again for taking the time to write this.

Ruth
Comment by Mark Lagendijk

Sure. Sorry I didn't think about that.

Comment by JAGH

Thanks for the technique, but my question is: is it normal that compiler fails silently when exists error in one imported file instead send me one error ?

JAGH
Comment by Mark Lagendijk

Hey Duncan,
Great work!

I have used your script and build 2 awesome LESS tools upon it:
-WinLess
-LessBuildEventScript

You can find them here: http://winless.org

WinLess is a compiler (with GUI) for LESS. WinLess can watch your LESS files, and automatically compile them when they have changed. WinLess is similar to LESS.app, which is a LESS application for OSx.

With the LessBuildEventScript you can easily integrate LESS within your Visual Studio projects. Your LESS files will then be compiled on each build.

I would strongly advise agains using dotlesscss based solutions or any other solutions which don't use less.js for the actual compiling. less.js is written by the author of the LESS language. In my expercience other solutions are often buggy and behind on newer features.

Comment by Duncan Smart

Looks good! I agree about the dotless stuff. Indeed one of the motivations of less.wsf was because dotless was lagging behind in the features.

Comment by Mark Lagendijk

Thanks. Could you maybe add a link to winless.org (with a brief description) to the post? I'm not sure how many people actually read all the comments.

Comment by mark

Awesome! Exactly what I needed. Thanks.

mark
Comment by jasondavisflason

I am looking to add support for LESS inside my Build script, I am using PHP's Phing which is just like the ANT build script. So this looks like the best solution, my build script can run commands line stuff, thanks

Comment by Duncan Smart

@Jagh Try this, using the ~"..." escaping technique:


.ui-datepicker-cover {
// hide from IE5 using hack
display: ~"none; display/**/: block";

// other browsers
position: absolute;
z-index: -1;
filter: mask();
top: -4px;
left: -4px;
width: 200px;
height: 200px;
}

Comment by Duncan Smart

No it shouldn't, not sure if it's a bug in my code, less itself, or the MS JScript engine...

Comment by Mark Lagendijk

Hey Duncan,
Users of WinLess have reported a few compiling issues. As you know WinLess uses your cool script to be able to use less.js to compile the LESS files. I have no clue what could cause these issues or even where they should be fixed (in less.wsf or less.js).
Maybe you could take a look at them and shed some light on the matter?

Note: I have tested all of these issues in the browser ( http://winless.org/online-less-compiler ) and can confirm that they don't appear there.

Issue 1:
When importing a less file the less.js changes the relative paths in that file so they will still work:
background-image: url('../images/bg.gif');

Should become:
background-image: url('subdirectory/../images/bg.gif');

But it becomes:
background-image: url('subdirectory/subdirectory/../images/bg.gif');


Issue 2:
"When an imported file is empty, the next imported is not processed

I use several imports to generate a single CSS file, and if a file output is empty (either it's been fully commented to test something, or just deleted its content), the next imported file is ignored in the output and no error is given."


Issue 3:
Using the '&' operator after a selector doesn't work properly. In the output the '&' parts get places before the subselector instead of after it:

#main {
#content {
.class {
position:absolute;
#container }
}
}

Should output:
#main #content .class { position: absolute; }
#container #main #content .class { position:relative; }

But this is what it does output:
#main #content .class #container { position: relative; }

Note: this usage of the '&'-operator isn't documented. In the documentation it only shows the operator being used before a selector. However, it does work when trying this example in the browswer.

Comment by JAGH

Excelent work, I use your script in my projects.

I have one question, but I'm not sure if it's for you or creator of fork of Less in Javascript.

This is:


tr .exampleLevel1 {
margin: 2px;
td .exampleLevel2{
margin: 3px;
&>span{
margin:7px;
}
}
}
}


It send me a compiler error using "&>span" but not for ">span", is it syntactically incorrect on LESS ?.

JAGH
Comment by Duncan Smart

@JAGH, glad to hear it!

Yes, > span is what you want, you don't need the extra "&". You typically use & for things like pseudo selectors such as &:hover

Comment by Mark Lagendijk

I can confirm that without the "&" it does work. The strange thing is that it does not throw an error when using the example with the "&" in the browser ( http://winless.org/online-less-compiler ), it outputs exactly the same with or without the "&".

Comment by Joseph McCullough

Hey man. I greatly appreciate the work you put into this. Most likely I'm going to end up just using Node.js, but I have to say, you should definitely gear this towards designers who might be less comfortable with complex installations (though what designers are using windows? ;) )

I downloaded your files and they work great. Seriously outstanding effort.

Comment by Sprockets und Less.js &laquo; PataGonia

[...] Windows hat Duncan Smart in seinem Weblog ein Paket zusammengestellt, da das für Less.js benötigte node.js nicht unter Windows [...]

Comment by David De Sloovere

I'm really interested in the Visual Studio’s T4 support. Any news on that yet?

Comment by Elze Kool

Works great! Altrough Less is made with speed in mind, compiling it into plain old css will always help

Comment by Sean

help! trying to use this with twitter.bootstrap. both your command line version . .and some GUI wrapper (winless) throw the below error. less bootstrap files are available on https://github.com/twitter/bootstrap/. root less file is bootstrap.less, it imports others.
Any help appreciated. The other methods .. using node.js (for windows) tried that . . no go. tried cygwin .. no go. could try to run a linux vm I guess. but this would be a lot more convenient :-)

cheers,
Sean.

C:\Development\precise\PreciseASP\PreciseASP\Content\less>lessc bootstrap.less bootstrap.css
ERROR:
type: Syntax
message: 'eval' is null or not an object
filename: bootstrap.less
column: -1
extract: ,, * Bootstrap v2.0.3
Press any key to continue . . .

Sean
Comment by Sean

did this fix (which is actually for winless) and it also fixed it for Duncans's stuff.
https://github.com/marklagendijk/WinLess/issues/1

Atm, I have no idea why, but when you change it to something like this it works:
The problem with the latest version of bootstrap is caused by the following mixin in mixin.less:

// IE7 inline-block
// ----------------
.ie7-inline-block() {
*display: inline; /* IE7 inline-block hack */
*zoom: 1;
dummy: huh;
}

add the dummy line, and it works.

Sean
Comment by Matt Andrews

Probably missing something simple here. I want to use the lessc alias but can't get it to work. I tried adding the directory I downloaded the code into to my PATH environment variable, but this didn't make any difference.

If I try running this:

lessc.cmd test.less output2.css

I get this:

/less.js-windows/lessc.cmd: line 1: ::For: command not found
/less.js-windows/lessc.cmd: line 2: @cscript: command not found

If I try just running 'lessc' (without the .cmd extension) I just get "lessc: command not found".

What am I missing here?!

Comment by Duncan Smart

Matthew, are you running this from a regular Command Prompt window? I have a feeling you may be running it in a bash console...

Comment by Matt Andrews

Aha! You're right -- I tend to use Git's Windows Bash shell for familiarity. When I tried it in cmd.exe it worked! Thank you, can't believe I didn't try that.

This is a great tool, thanks so much for the work.

Comment by Pedro M.

Hi Duncan,
I'm using the lessc.wsf to compile my main.less file, but the result is strange and i'm not figuring out what is the problem.

The problem is that the content of my main file is repeating 5 times, when compiles it does the imports of the files ok.
For example:

@import "less/bootstrap.less"; //add the compiled content OK

all my styles here // repeates 5 times

-------
I done some tests like. My file have 435 lines and repeats 5 times my styles, with 434 lines it works ok.
There is some characters/size/lines limitation for the compiler?

There some way can i send you my test?

Pedro M.
Comment by Duncan Smart

Pedro, I'm not aware of any limitations that would result in what you're seeing. Very odd.

Comment by Alex

Less watcher batch script (copy-paste to less-watch.cmd)

@ECHO OFF
CLS
:: Less watcher
:: Author: Alex Skrypnyk (alex.designworks@gmail.com)
:: Modified: 24/09/2012
:: Version 1.0
:: lessc must be installed
:: [Original blog post](blog.dotsmart.net/.../).
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
echo Starting watching %1. Output will be writtem to %2.
echo Use Ctrl-C to stop watching.
for /L %%n in (1,0,10) do (
call "%~dp0lessc.cmd" %*
timeout /T 1 /NOBREAK>nul
)
ENDLOCAL

Hope this will help someone.

Comment by Daniel

Great tool, thanks a lot. But I just found a tiny glitch:
The script fails for files with >= 35 @import directives:

ERROR:
type: Syntax
message: 'eval' is null or not an object
filename: C:\web\styles.less
column: -1
extract: ,,@import "page-basic.less";

Daniel