Expert

Coen Adrien van Driel

I'm a software consultant and an Azure, DevOps & Testing enthusiast with a focus on Microsoft technologies.

Setting Up LESS In ASP.NET Core

December 26, 2019

If you want to use LESS style sheets in your ASP.NET Core application you need to take some additional steps to get things working. In ASP.NET Core the use of LESS is not supported out of the box and you will need to set up some addons to get rolling.

There are multiple options for getting LESS to work in ASP.NET Core, in this article I will explain how to use LESS with Gulp, that you can also use to minify and bundle your other scripts and styles. Gulp is a tool for automating workflows that can be used for a lot of other things as well, but that is for a later article.

Why use LESS in ASP.NET Core?

In case you are not familiar with LESS, it is worth having a look at if your project requires CSS for styling. LESS stands for Leaner Style Sheets and is a CSS pre-processor which can help in writing less and better structured CSS. LESS requires .LESS files that are then compiled into .CSS files by the LESS compiler. You can write plain CSS in your LESS files so you can start off by writing simple plain CSS in your .LESS files and adding LESS features where you need them.

I personally use LESS because it allows you to use variables in your CSS (like a global color scheme that you can reference from all your LESS files) and because it makes CSS nesting a lot more structured. To learn more on LESS you can visit lesscss.org, if you want to play around with LESS there is an online LESS compiler for that.

Below you find an example of using variables in LESS

//LESS input 
@@width: 10px;
@@height: @@width + 10px;

#header {
  width: @@width;
  height: @@height;
}


//CSS output
#header {
  width: 10px;
  height: 20px;
}

Below you find an example of using nesting in LESS

//LESS input 
#header {
  color: black;
  .navigation {
    font-size: 12px;
  }
  .logo {
    width: 300px;
  }
}

//CSS output
#header {
  color: black;
}
#header .navigation {
  font-size: 12px;
}
#header .logo {
  width: 300px;
}

The sample project setup

To demonstrate LESS in .NET Core we start out with an empty ASP.NET Core Web Application project, in this case based on the .NET Framework and in Visual Studio 2019.

less in asp.net core project 1

Also, you will need to use the Visual Studio Task Runner Explorer, you can find the Explorer via View > Other Windows > Task Runner Explorer. At this point your project should look like the project below.

less in asp.net core project 2

Let’s create a Styles folder where we will create our LESS files in the root of the project, you can of course choose your own location if needed. Then create a wwwroot-folder where we will copy our CSS files into. Next we need to create a Gulp file. Gulp is a tool that we use to run tasks in Visual Studio and in this case we use it to run the task of building LESS files and later also to bundle and minify them. You can read more on Gulp at gulpjs.com.

A gulpfile is a javascript file that is recognized by the Visual Studio Task Runner. Create a javascript file (using right click on the project then Add > New Item > Javascript File) with the name gulpfile.js in the root of the project. The Task Runner Explorer will pick up on the file and show the file in the Explorer. By now we have the following project.

less in asp.net core project 3

We now need to add the Gulp dependencies we need to in order to build the LESS files. To do this, we create a .json file in the root of the project (using right click on the project then Add > New Item > JSON File) called package.json. In package.json we declare the following dependencies.

{
  "name": "asp.net",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-less": "3.1.0"
  }
}

After saving the file, and waiting for some seconds, Visual Studio should automatically start downloading the packages to the Dependencies folder under NPM. The gulpfile.js in the Task Runner Explorer should no longer show the failed-status and now just show No tasks found, because our gulpfile.js is still empty. You will need to refresh the Task Runner Explorer for this.

less in asp.net core project 4

Compiling your LESS files to CSS

With our project foundation done, let’s create a small LESS (using right click on the Styles folder then Add > New Item > LESS Style Sheet) file in the Styles folder called Styles.less.

In Styles.less we enter the following LESS code as an example:

@@width: 10px;
@@height: @@width + 10px;

#header {
    width: @@width;
    height: @@height;
}

We can then start working with the gulpfile.js to start creating tasks for our Task Runner to run. The first task is to simply compile the LESS code into CSS code. We can achieve that by entered the following code into our gulpfile.js.

var gulp = require("gulp"),
	less = require("gulp-less");

gulp.task("compile:less", function () {
    return gulp.src("Styles/**/*.less")
		.pipe(less())
        .pipe(gulp.dest("./wwwroot//css"));
});

The code first declares the packages we need and then we create the task that compiles our LESS code into CSS files using the desired paths. As a naming convention we name our task ‘compile:less’ because we can also have non-LESS compilation tasks.

You can now run the Task from the Task Runner Explorer which should result in a new styles.css file in your wwwroot.

less in asp.net core project 5

If we also want to rename our resulting CSS files, in this case to lower case, we can use the gulp-rename and change-case-package to achieve this. You will need to delete to old Styles.css file in the wwwroot first to see the lowercase version.
To our package.json we add:

"gulp-rename": "1.2.2",
 "change-case": "3.0.0"

And to our gulpfile.js we add both packages to the list of required packages and we add the rename-pipe to our compile:less task:

var gulp = require("gulp"),
    less = require("gulp-less"),
    rename = require("gulp-rename"),
    changeCase = require("change-case");

gulp.task("compile:less", function () {
    return gulp.src("Styles/**/*.less")
        .pipe(less())
        .pipe(rename(function (path) {
	        path.dirname = changeCase.lowerCase(path.dirname);
	        path.basename = changeCase.lowerCase(path.basename);
	        path.extname = changeCase.lowerCase(path.extname);
        }))

        .pipe(gulp.dest("./wwwroot//css"));
});

This results in the following setup:

less in asp.net core project 6

Minifying your LESS files

At this point we have compiled our LESS files into plain CSS files which are now in the wwwroot-directory of our project. To minify the LESS files we have two options, we can minify the LESS files in the same process as the compilation, or we can compile the LESS files first and then minify the plain CSS files.

The first solution to directly compile and minify your LESS files is a faster process but you will lose the plain, readable, CSS in the process as it directly converts your LESS to minified CSS. A solution to this would be to use ‘source maps’ but that is for another article.

To minify your LESS files within the same process as compiling LESS into CSS we need to add a few lines of code in our package.json and gulpfile.js.

To package.json add the ‘gulp-clean-css’ package and save the file. The package.json now looks like this:

{
  "name": "asp.net",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-less": "3.1.0",
    "gulp-rename": "1.2.2",
    "change-case": "3.0.0",
    "gulp-clean-css": "4.2.0"
  }
}

To our gulpfile.js we add the new package using cleanCss = require("gulp-clean-css") and add .pipe(cleanCss({ compatibility: 'ie8' })) to our task.
The gulpfile.js now looks like this:

var gulp = require("gulp"),
    less = require("gulp-less"),
    rename = require("gulp-rename"),
    changeCase = require("change-case"),
    cleanCss = require("gulp-clean-css");

gulp.task("compile:less", function () {
    return gulp.src("Styles/**/*.less")
        .pipe(less())
        .pipe(rename(function (path) {
	        path.dirname = changeCase.lowerCase(path.dirname);
	        path.basename = changeCase.lowerCase(path.basename);
	        path.extname = changeCase.lowerCase(path.extname);
        }))
        .pipe(cleanCss({ compatibility: 'ie8' }))
        .pipe(gulp.dest("./wwwroot//css"));
});

If you run the task in the Task Runner Explorer again you should see that our outputted styles.css in the wwwroot now contains minified CSS.

#header{width:10px;height:20px}
less in asp.net core project 7

The second solution is to separate the process of compilation and minifying, we then keep the original plain CSS files next to our minified versions in the wwwroot.

First, let’s undo the changes from the first solution by removing the gulp-clean-css references in package.json and gulpfile.js. Our starting point is again, that we just compile the LESS into CSS and nothing more.

To separate the minifying from the compiling we need to create a second task for the Task Runner. This task requires the ‘gulp-cssmin’ package so we need to add it to our package.json

Our gulpfile.js must then be updated with the gulp-cssmin-reference at required packages and we can create a second task for minification.

gulp.task("minify:css", function () {
    return gulp.src(["./wwwroot/css/**/*.css", "!" + "./wwwroot/css/**/*.min.css"], { base: "." })
		.pipe(cssmin())
		.pipe(rename({ extname: ".min.css" }))
		.pipe(gulp.dest("."));
});

var gulp = require("gulp"),
    less = require("gulp-less"),
    rename = require("gulp-rename"),
    changeCase = require("change-case"),
    cssmin = require("gulp-cssmin");

gulp.task("compile:less", function () {
    return gulp.src("Styles/**/*.less")
        .pipe(less())
        .pipe(rename(function (path) {
	        path.dirname = changeCase.lowerCase(path.dirname);
	        path.basename = changeCase.lowerCase(path.basename);
	        path.extname = changeCase.lowerCase(path.extname);
        }))
        .pipe(gulp.dest("./wwwroot//css"));
});

gulp.task("minify:css", function () {
    return gulp.src(["./wwwroot/css/**/*.css", "!" + "./wwwroot/css/**/*.min.css"], { base: "." })
		.pipe(cssmin())
		.pipe(rename({ extname: ".min.css" }))
		.pipe(gulp.dest("."));
});

After saving the gulpfile.js we see the new task also being shown in the Task Runner Explorer. When we run this new minify-task we end up with 2 CSS files in our wwwroot-directory, one plain, one minified. Make sure that you run the compile:less task before running the minify:css task.
With our styles.less compiled to CSS and minified we now have the following project:

less in asp.net core project 8

Bundling your LESS files

To bundle multiple LESS files we essentially compile our LESS files to CSS first, and then bundle the CSS files. We can also minify our CSS files before the bundling, but for this example we bundle first and then minify all CSS files, bundled or not.

To bundle some CSS files we first need to create an extra LESS file for demonstration purposes, so lets create a second LESS file in the Styles folder named ‘Second.less’, as its contents we use the nesting-example from the first part of this article.

#header {
    color: black;

    .navigation {
        font-size: 12px;
    }

    .logo {
        width: 300px;
    }
}

When we now run our compile:less task we see both files being compiled and outputted to the wwwroot-directory. The bundling is done via another task in the gulpfile.js, for this we need the filepaths of the files we want to bundle and the output path. This task requires the ‘gulp-concat’ package as shown in the example. Add the package to your package.json and the top of the gulpfile.js for its required packages.
Then add the new bundling task to your gulpfile.js:

gulp.task("bundle:css", function () {
	return gulp.src([
			"./wwwroot/css//styles.css",
			"./wwwroot/css//second.css"])
		.pipe(concat("bundle.css"))
        .pipe(gulp.dest("./wwwroot/css"));
});

After running this task we have the bundle.css file in our wwwroot-directory ready to be minified. Starting the minifying task results in all CSS files being minified, so we can start the task and find our bundle.min.css as a result.

less in asp.net core project 9

Automatically cleaning the wwwroot before compiling new LESS files

If you make changes to your LESS files and run all tasks, existing CSS files will be overwritten. However, removing LESS files from our styles-folder will not result in deletion of the corresponding CSS file in the wwwroot. To solve this we can clean our wwwroot-directory before running the compile:less task.

Create a new task that uses the ‘gulp-clean’ package and references your CSS output directory. Add the package to your package.json and the top of the gulpfile.js. Then add the task below to your gulpfile.js.

gulp.task("clean:css", function () {
    return gulp.src("./wwwroot//css", { read: false })
		.pipe(clean());
});

When running this task, all files in the wwwroot/css-folder are deleted.

less in asp.net core project 10

Linking all tasks together

We currently have a clean, compile, bundle and minify-task for our LESS development. Wouldn’t it be easy to just have to start a single task to run all of those? We can using the run-sequence package in gulp!
After adding the run-sequence package we add a new task (to rule them all), with the following command:

gulp.task("process:less", function (callback) {
	runSequence("clean:css", "compile:less", "bundle:css", "minify:css", callback);
});

We now only have to start the process:less-task to run the entire process of cleaning, compiling, bundling and minifying.

less in asp.net core project 11

Automatically update your CSS files after editing your LESS files

Manually starting all tasks, or just one process-task, from the Task Runner Explorer every time you have changed your LESS file is not a very efficient development process. To automate these tasks we can use the Watch-feature.
At this point you could have guessed that this needs a new task, and here it is:

gulp.task("watch:less", ["process:less"], function () {
    gulp.watch(paths.lessSrc, ["process:less"]);
});

We reference our earlier made process:less that contains all the tasks related to LESS development. Our Task Runner Explorer recognizes the gulp.watch feature and starting this watch-task once, will run the task on every change to any LESS file.

Starting your watch-task at the start of your LESS development means the watch will keep running continuously, detecting any change to your LESS file and updating your wwwroot.

Besides creating watch-tasks, we can also add a bindings that will make any task run before building, after building, on cleaning or on project open. We simple need to add a single line to the top of our gulpfile.js, lets say we want to run all tasks before building the project.
We add the following line to the top of our gulpfile.js if we want to run our process:less-task before building the project.

/// 
This binding will also be shown in the Bindings-tab in the Task Runner Explorer.

Completing and cleaning up the gulpfile.cs

We now have a completed LESS sample project where we have used Gulp to clean, compile, bundle and minify LESS files to CSS. Our gulpfile.js does still contain some duplication when it comes to source and target-paths, so the last thing we need to do is create some global variables to centralize our paths.
After moving our paths to global variables we end up with the finished project.

Gulpfile.js

/// 
var gulp = require("gulp"),
    less = require("gulp-less"),
    rename = require("gulp-rename"),
    changeCase = require("change-case"),
    cssmin = require("gulp-cssmin"),
    concat = require("gulp-concat"),
    clean = require("gulp-clean"),
    runSequence = require("run-sequence");

var paths = {};
paths.webroot = "./wwwroot/";
paths.cssTarget = "./wwwroot//css";
paths.lessSrc = "Styles/**/*.less";
paths.minCss = paths.webroot + "css/**/*.min.css";
paths.cssSrc = paths.webroot + "css/**/*.css";

gulp.task("compile:less", function () {
	return gulp.src(paths.lessSrc)
		.pipe(less())
		.pipe(rename(function (path) {
			path.dirname = changeCase.lowerCase(path.dirname);
			path.basename = changeCase.lowerCase(path.basename);
			path.extname = changeCase.lowerCase(path.extname);
		}))
		.pipe(gulp.dest(paths.cssTarget));
});

gulp.task("minify:css", function () {
	return gulp.src([paths.cssSrc, "!" + paths.minCss], { base: "." })
		.pipe(cssmin())
		.pipe(rename({ extname: ".min.css" }))
		.pipe(gulp.dest("."));
});

gulp.task("bundle:css", function () {
	return gulp.src([
			"./wwwroot/css//styles.css",
			"./wwwroot/css//second.css"])
		.pipe(concat("bundle.css"))
		.pipe(gulp.dest("./wwwroot/css"));
});

gulp.task("clean:css", function () {
	return gulp.src(paths.cssTarget, { read: false })
		.pipe(clean());
});

gulp.task("process:less", function (callback) {
	runSequence("clean:css", "compile:less", "bundle:css", "minify:css", callback);
});

gulp.task("watch:less", ["process:less"], function () {
	gulp.watch(paths.lessSrc, ["process:less"]);
});

Package.json

{
  "name": "asp.net",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-less": "3.1.0",
    "gulp-rename": "1.2.2",
    "change-case": "3.0.0",
    "gulp-cssmin": "0.2.0",
    "gulp-concat": "2.6.1",
    "gulp-clean": "0.3.2",
    "run-sequence": "1.2.1",
    "gulp-watch": "4.3.6"
  }
}

less in asp.net core project 13

Thanks for reading!

If you want to stay in the loop when I post new articles, follow me on facebookFacebook or twitterTwitter Was this article helpful or do you have any questions? Leave a comment!

Check out other similar articles