Apr 25, 2014

Customizing Twitter Bootstrap with the Lesscss Gradle plugin

Abstract

In this article I will show you how to customize the Twitter Bootstrap style sheets and compile them with the Lesscss Gradle plugin.

You won’t have to change the original sources. You won’t even have to download them manually.

I’ll also show how you can inspect the LESS sources in your browser (in stead of the generated CSS) and how to get productive with automatic compilation.

Quick start

  1. If you already have gradle:

    There’s only one file to download: build.gradle :)
    Save it in the directory less-bootstrap and then:

    cd less-bootstrap
    gradle init
    gradle lesscDaemon --info
  2. You don’t have Gradle yet:

    Clone the github project with git clone https://github.com/houbie/less-bootstrap or download and unpack the zip distribution and use the included gradle wrapper:

    cd less-bootstrap
    gradlew init
    gradlew lesscDaemon --info

Now you can:

  1. open less-bootstrap/web/bootstrap-examples/theme/index.html (or an other example)

  2. change less-bootstrap/web/less/custom-variables.less

  3. reload the page to see the changes

Tip
Before reloading the page in your browser, you’ll have to wait a few seconds until the style sheets are compiled. If you don’t like waiting (who does?), see Speeding up compilation

Exploring the examples

The examples are located in less-bootstrap/web/bootstrap-examples. When you open f.e. less-bootstrap/web/bootstrap-examples/theme/index.html and inspect the Learn more button in Google Chrome, you will see something like this:

Inspecting an element in Chrome
Figure 1. Inspecting an element in Chrome

The inspector shows the LESS sources because the the lessc task is configured to generate source maps:

Enabling source maps in build.gradle
lessc {
    ...

    //generate source maps so that Google Chrome shows the less source in the inspector i.s.o. the raw CSS
    options.sourceMap = true

    //the generated css file contains a reference to the source map, this reference will be relative to
    //the sourceMapBasepath in this case it will be in the same directory as the css itself
    //(default location of source maps)
    options.sourceMapBasepath = file("$webDir/css").absolutePath

    //we could try to specify the sourceMapxxx options so that the browser can load the LESS sources
    //directly, but this is not trivial since our sources reside in different locations
    //therefore we just copy all the less source code into the source map file
    options.sourceMapLessInline = true

}
Note
At the moment of writing, Chrome is the only browser that supports source maps for CSS files. This blog shows how to enable CSS source maps if they are not enabled by default.

Customizing the style sheet

Where is the code?

We would like to change the background color of the Learn more button, but how do we locate its definition?

Again, by inspecting the element in Chrome, we see that the background color is defined in buttons.less.

The button's background colour
Figure 2. The button’s background colour

When we double-click buttons.less, the source is opened in the inspector:

Buttons.less
Figure 3. Buttons.less

We see that the variable that we need to change is btn-primary-bg. Unfortunately, we cannot navigate further anymore in the inspector, so we have to search manually.

The bootstrap source files can be found in less-bootstrap/build/bootstrap/web-app/less. A text search leads us to mixins.less for the button-variant definition and to variables.less for the background color variable:

@btn-primary-bg: @brand-primary;

Albeit not perfect, it is a lot easier to find the code in this way than if we would only see the raw CSS in the inspector.

At this point we have to decide whether we want to change only the color of the primary buttons, or if we want to change the primary brand color.

Modifying styles

There are 3 ways to modify the style sheets:

  1. Overwrite variables in build.gradle

  2. Modify a copy of the original source

  3. Create our own customization LESS file

Overwriting variables in build.gradle

The lesscss compiler allows us to declare new or overwrite existing variables via the commandline or in build.gradle:

lessc {
    ...
    options.modifyVars = ['brand-primary': 'purple']
}

Overwriting variables this way can be useful if you want f.e. to use a different color scheme for development builds than for release builds, but it is not suitable for more involved customizations.

Modify a copy of the original source

The lessc task is configured to lookup LESS files first in less-bootstrap/web/less:

lessc {
    ...
    sourceDir "$webDir/less", "$buildDir/bootstrap/web-app/less"
}

This means that if we would copy variables.less to less-bootstrap/web/less and modify it, it will take precedence over the original file.

However, when we would like to upgrade to a newer bootstrap version, we would need to apply all our changes again in the new file, which is far from ideal.

Note
Changing brand-primary in our copy of variables.less won’t have any effect, it will always be overridden by the value in build.gradle!

Create our own customization LESS file

The main LESS file, bootstrap.less, consists of only import statements. If we would append a few import statements to include our own customization files, we wouldn’t have to change the original LESS code. This is exactly what the init task does when it unpacks the bootstrap sources:

//have our custom less files imported into bootstrap.less and theme.less
file("$buildDir/bootstrap/web-app/less/bootstrap.less").text += '''
@import "custom-variables.less";
@import "application.less";'''

file("$buildDir/bootstrap/web-app/less/theme.less").text += '@import "custom-variables.less";'

Now you only have to keep the customizations in your project’s source repository. Furthermore, switching to another version of bootstrap becomes trivial.

Define your own semantics

As pointed out in this blog, you should use html elements and/or CSS classes that outline the structure of your documents.

/less-bootstrap/web/less/application.less defines a sample article structure that is used in less-bootstrap/web/bootstrap-examples/starter-template/semantics.html.

Again, we are extending bootstrap without changing the original sources.

Speeding up compilation

Although the lesscss compiler is the fastest Java LESS compiler, it is still very slow compared with the original node.js compiler.

Fortunately, you can use the node.js lessc compiler in combination with the Lesscss Gradle Plugin:

lesscDaemon {
    engine = 'commandline'
    lessExecutable = '/opt/local/bin/lessc'
}

Now, your style sheets will typically be compiled by the time you switched from the source editor to the browser.

The lessExecutable is only required when lessc is not on your path. In windows you should include the .bat or .cmd extension.

You can read here how to install the node.js lessc compiler.

Tip
Use the fast node.js compiler in the lesscDaemon task to save time when developing the style sheets. Keep the default (java rhino based) compiler in the lessc task to avoid installing node.js on your CI server and to have deterministic builds that always use the same compiler version.

Mar 28, 2014

Follow-up: JavaScript on the JVM, experimenting with Nashorn

The release of JDK8 was a good opportunity to test again how fast the Nashorn JavaScript engine is in compiling Less style sheets.
I hoped that performance improved since I last tried an early access build, but alas...

Setup

I recently released a new version of lesscss that supports 3 execution engines:
  • rhino 1.7R4: it runs a pre-compiled version of less.js in the highest optimization level (9)
  • nashorn: the JDK8 built-in version, which does not (yet) support pre-compilation nor optimization
  • node.js lessc: prepare a commandline string and execute it with Runtime.exec
I compiled the Twitter Bootstrap style sheets using different engines.
I used my mac book (2.66GHz core i7 with 8GB memory and JVM args -Xms512m -Xmx1500m -XX:MaxPermSize=320m).
For each run I instantiated one engine for 15 consecutive compilations.

As you can see, both rhino and nashorn perform better when the JVM warmed up, but the differences in performance are huge:

Time needed to compile Twitter Bootstrap (seconds)
  The first row includes JavaScript compilation in case of Nashorn
# Rhino
(optimized)
Nashorn node.js
1 3.9 18.4 0.9
2 1.9 7.9 0.6
3 1.7 6.1 0.6
4 1.6 5.8 0.6
5 1.4 5.0 0.6
6 1.4 4.9 0.6
7 1.5 4.5 0.6
8 1.4 5.3 0.6
9 1.3 5.8 0.6
10 1.3 4.4 0.6
11 1.0 4.0 0.6
12 1.0 3.8 0.6
13 1.0 3.5 0.6
14 0.9 3.1 0.6
15 0.9 2.8 0.6

Conclusions

Although this is not a real benchmark, the figures differed less then 20% between runs and the trends are clear (at least for this use case):
  • Rhino with optimizations is still a lot faster then nashorn
  • While rhino got faster on JDK8, nashorn seems to got slower (see previous blog)
  • There is a big gap with node.js, despite the overhead of spawning a new process and writing its output to disk