Compiling Bootstrap from LESS with Grunt in Visual Studio

By Sergey Nosov

March 14, 2015

What is LESS and why it is useful

Since 1996, Cascading Style Sheets (CSS) has been the web’s premier instrument for describing how documents should look, separately from what documents contain. CSS language is very lean; it does not contain functions and its variable use is limited and currently not well supported in all popular browsers.

For making CSS more dynamic and digestible in large projects, various CSS pre-processor languages were invented. LESS, as one of these languages, allows writing style instructions using full power of CSS supplemented by such programming features as variables, extents, mixins, loops, and others. The resulting dynamic code is then compiled to plain CSS to be interpreted by browsers.

Bootstrap saves time

To expedite projects, contemporary Internet and intranet front-end developers rarely start CSS designs from scratch. Instead, they build on top of frameworks that already have commonly used elements, such as buttons, toolbars, and grid layouts, implemented. One of the front-end frameworks is Bootstrap by Twitter. Microsoft became impressed with Bootstrap so much, they have been including it in default ASP.NET web project templates since Visual Studio 2013.

Bootstrap developers use LESS, which is why LESS is a natural choice for customizing and extending Bootstrap in your projects.

To get started, you need to replace the CSS-only Bootstrap package in your Visual Studio ASP.NET project, with the package containing Bootstrap LESS source code. Open the NuGet Package Manager Console and run the following two commands:

Uninstall-Package bootstrap

Install-Package Twitter.Bootstrap.Less

You can also do this from the “Tools–Extensions and Updates…” window. After the new package is installed in your project, you will find the Bootstrap LESS source code files in the project’s Content/bootstrap directory.

While LESS files are understood by some browsers or by browsers with specially configured extensions, the mainstream way of using LESS is by compiling it into CSS, which brings us to the next section.

NPM and Grunt

There are a few different ways of compiling LESS files. The Web Essentials plugin for Visual Studio, for instance, is capable of compiling LESS files on-the-fly. Another way of doing it is the way Bootstrap development team does it, with Grunt task runner.

Bootstrap’s in-house compilation routine is very elaborate; it builds the whole distribution package, including documentation. We will be using a simplified method that you can adjust to suit your needs, or you can clone the entire Bootstrap repository from GitHub, to get the whole kitchen sink, in case you want absolutely no deviations from the official Bootstrap output.

Task runners, Grunt and Gulp, are the JavaScript world loose equivalents of MSBuild. They allow scripting of consecutive tasks: building, copying, cleaning, minifying, etc. Generally, you pick Grunt or Gulp; in this article we are using Grunt; you could use Gulp as well, it is just the semantics would be a little different.

But wait, Grunt and Gulp are JavaScript tools, and JavaScript usually runs in web browsers. Do we run Grunt in web browser, then? No, this is where Node.js comes into view. Node.js is a cross-platform environment for running JavaScript outside web browser; you can run it on the server or on your development machine. So, as the next step, if you do not have it already, install Node.js and restart your computer.

Once Node.js environment is operational, it is time to install packages with other tools that we need. Package installation is simplified with package managers. Where Visual Studio includes NuGet package manager, Node.js comes with NPM package manager. The default Node.js installation puts NPM tools location into the environment %PATH% variable, so that the functionality is available from any Command Prompt window.

NPM is a hierarchical package manager, when it installs a package, it will also pull all other packages that are listed as dependencies, and dependencies of dependencies, and so on and so forth, and install them into a tree-like structure of subfolders. In contrast, flat package managers, like Bower, will install only the particular packages requested. As such, while NPM and Bower somewhat overlap in functionality, NPM is mostly used on the development side, to save developers’ time by automatically pulling everything needed, while Bower (that we are not going to use in this article) is good on the deployment side, where a list of needed packages has stabilized and it is important to have no surprises in package count and locations and no package duplication.

Launch command prompt and change (cd) to your Asp.NET project’s home directory. To figure out which folder that is you can right click on the project in the Solution Explorer, and pick “Open Folder in File Explorer.”

The next step, creating a package.json file for the project, is for Visual Studio versions before 2015 (newer versions should already create that file for you; at the time this article was written Visual Studio 2015 has not yet been released). In the command prompt, in the project directory, run the following command.

npm init

NPM init will ask you a few questions about project name, description, repository location, etc., and make a package.json file for the current folder. The important fields are “name” and “version”, also put something in “description” and “author”.  Then, edit the file, delete unneeded fields, and add the "private": true directive. Your resulting intermediate package.json file contents will look something like the following.

  {
  "name":  "MyWebProject",
  "version": "1.0.0",
  "description": "My web project that does awesome things and looks great",
  "author": "Sergey Nosov  <myemail@mydomain.com>",
  "private": true
  }

The "private": true directive, prevents you from accidently publishing the package for the whole world to see, and suppresses warning for things, not normally needed in private packages, missing.

You could also create the package.json file by-hand without running NPM init; NPM init does nothing magical, it just creates the file.

Next, we install needed NPM packages. First is the Grunt command line interface (CLI). It is recommended that Grunt CLI is installed globally. Global packages on Windows computers are installed in the %AppData%/npm folder. If you ever mess-up your global packages repository, and want to start over, simply delete the %AppData%/npm folder, and run the following command to clear cache.

npm cache clean

The next command installs Grunt CLI. The “-g” switch is what tells NPM to install it globally.

npm install -g grunt-cli

After this, we install three local project packages with the following command. Make sure you are still in the project folder, before running it.

npm install grunt grunt-contrib-less less-plugin-autoprefix --save

The “--save” switch tells NPM to update the package.json file and list just installed packages as project dependencies. The packages and their dependency trees are installed into the node_modules project subfolder. It is safe to delete the node_modules folder, and re-download packages as you wish. To install the packages—on this or maybe another development computer where the project was cloned—already listed in the package.json, there is no need to specify the packages again, simply run the following command form the project folder.

npm install

If you deleted or did not have the project node_modules folder before running npm install, NPM will download all local dependencies listed in the package.json and their sub-dependencies. Otherwise it will download whatever is missing.

Your final package.json file contents will look something like the following.

{
    "name":  "MyWebProject",
    "version": "1.0.0",
    "description": "My web project that does awesome things and looks great",
    "author": "Sergey Nosov <myemail@mydomain.com>",
    "private": true
    "dependencies": {
        "grunt": "^0.4.5",
        "grunt-contrib-less": "^1.0.0",
        "less-plugin-autoprefix": "^1.4.0"
    }
}

The dependencies list includes three packages needed for our project: grunt task runner, grunt-contrib-less LESS compiler, and less-plugin-autoprefix package for working with browser specific CSS prefixes.

For the next few steps let us go back to the Visual Studio.

Include the package.json file in the Visual Studio project, if not already included. Also this is a good time to create and include in the project a README.md file. In that file you can put instructions on how to get the configuration going, in case you need to move the project to another computer or give it to a different developer.

Under the Content folder find the Site.css file and rename it to Site.less. Open the Site.less file and at the top of it add the following line.

@import "bootstrap/bootstrap.less";

This import directive pulls the main bootstrap.less file into our Site.less. We can override and extend bootstrap anywhere in the Site.less (or other imported files) after the import directive. We are not going to manually touch any of the files in the Content/bootstrap folder, that way you can install fresh versions of the Twitter.Bootstrap.Less NuGet package at any time and not lose your changes.

We are not going to have separate Bootstrap CSS files ether, all LESS source code is going to be compiled in together.

For ASP.Net MVC projects, find the BundleConfig.cs file under the App_Start folder, and remove the "~/Content/bootstrap.css" from the "~/Content/css" style bundle (for ASP.Net WebForms projects, edit the Bundle.config XML file, in the project root folder, and remove the <include path="~/Content/bootstrap.css" /> line). We will compile bootstrap together with site specific markup, as such we no longer need to bundle the bootstrap.css separately.

Next, right click on your project in the Solution Explorer, and choose “Add–JavaScript File.” Name the file Gruntfile and click the OK button. Edit the newly created Gruntfile.js so its content is as follows.

module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        less: {
            dev: {
                options: {
                    sourceMap: true,
                    dumpLineNumbers: 'comments',
                    relativeUrls: true,
                    plugins: [
                            new (require('less-plugin-autoprefix'))({ browsers: ["last 2 versions"] })
                    ]
                },
                files: {
                    'Content/Site.debug.css': 'Content/Site.less',
                }
            },
            production: {
                options: {
                    compress: true,
                    relativeUrls: true,
                    plugins: [
                            new (require('less-plugin-autoprefix'))({ browsers: ["last 2 versions"] })
                    ]
                },
                files: {
                    'Content/Site.css': 'Content/Site.less',
                }
            }
        },

    });

    grunt.loadNpmTasks('grunt-contrib-less');

    grunt.registerTask('default', ['less']);
    grunt.registerTask('production', ['less:production']);
    grunt.registerTask('dev', ['less:dev']);
};

Go back to the project directory command prompt window. And run the following command.

call Grunt

This will execute the tasks we just created, and if everything goes well you will end up with new Site.css and Site.debug.css files (which you want to include in the Visual Studio project). These are the results of the LESS compilation. The file with “debug” in the name is more verbose, the one without is more compact for production use.

Write your own LESS

Do you remember when we renamed the Site.css to Site.less? We could do that because LESS is a superset of CSS. Everything that is valid in CSS is valid in LESS. But LESS is also so much more; let us dive in.

Open the Site.less file in Visual Studio. See that the body tag is decorated with padding-top: 50px attribute? That is done because the Bootstrap top navigation bar overhangs the rest of the content, and in current versions this overhang happens to be 50 pixels. What if overhang changes and you still have the 50 pixels hard-coded in your CSS? Your layout will be out of alignment.

Simply replace the 50px with @navbar-height variable that Bootstrap defines, and you now can be sure that you always compensate for the right overhang.

Let us go a step further, and change something more visual in our design. Add the following line at the bottom of the Site.less file, to override the Bootstrap variable that controls the body background color.

@body-bg:               #f89422;

You resulting Site.less file will look something like the following (other lines removed for brevity).

//** Pull Bootstrap in
@import "bootstrap/bootstrap.less";

//**Removal of Bootstrap top navbar overhang
body { padding-top: @navbar-height; }

//** Background color for `<body>`.
@body-bg:	#f89422;

Compile LESS again, with call Grunt, and run the site to see the changes, you now should have a nice orangey-yellow background.

Automating LESS compilation

We now have the infrastructure for compiling LESS sources into CSS fully functional, but so far we had to manually call Grunt every time we changed some LESS code.

One way to make it run automatically is by placing the call to Grunt into the project’s Pre-build event.

Right click on your project in the Solution Explorer, and choose “Properties.” Activate the “Build Events” tab, and in the “Pre-build event command line” box add the following two lines.

cd $(ProjectDir)

call grunt --no-color

Now, every time you build the project, your LESS source code is compiled as well.

Visual Studio Task Runner Explorer plugin is available for more sophisticated interaction with various Grunt or Gulp tasks.

It is your choice whether to use various Visual Studio plugins; no question, they can make you much more productive at your job. However, we think that by following our manual steps, you now better understand how these plugins work under-the-hood; and also if you have to use Visual Studio environments where you have no control over which plugins are installed you now know what to do.

In conclusion, we hope that those who have not used LESS before learned how to harness its power for ASP.Net projects, and that you now have a better idea how different pieces of JavaScript development environment come together.

 

Copyright © 2012-2021 www.orderfactory.com. All rights reserved.
"Configuring Windows 2008 R2 Web Server" is an independently published book and is not affiliated with, nor has it been authorized, sponsored, or otherwise approved by Microsoft Corporation. Windows is a registered trademark of Microsoft Corporation in the United States and other countries.

Privacy Policy