Stu Ratcliffe

Autoprefixing CSS files in ASP.NET Core Web Applications


Recently I started migrating a number of small web apps over from MVC 5 to Core 1.0, and one of the few things that I had to do differently in the new framework was CSS / JS bundling and minification.

The original way of bundling and minifying in ASP.NET Core was using gulp or grunt to create a series of tasks that could be included as part of the build or publish pipeline to run automatically on the specified files. Microsoft have an excellent article on how to set this up here so I won't go in to any more detail than that.

There is nothing wrong with this method, but if you are not familiar with javascript this can be a little daunting to begin with, and there is definitely a much easier way!

The Bundler and Minifier Extension

Mads Kristensen has provided an excellent extension for visual studio to make this process a lot easier for us. Rion Williams has already written a brilliant article on using this extension here. He also includes a section on how to configure it for use with VS Code if you prefer using a more lightweight IDE than full-fat visual studio, or you prefer to do your .NET development on a non-windows platform.

The basics of the process are as follows.

  1. Add the BundlerMinifier.Core package to the tools section of your project.json file.

  2. Add a bundleconfig.json file to the root of your project and configure your bundles as appropriate for your needs

  3. Add the dotnet bundle command to your build / publish pipeline to automate the process

A sample bundleconfig.json file is as follows:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/atom-one-dark.css",
      "wwwroot/css/site.css"
    ],
    "minify": {
      "enabled": true
    }
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/highlight.pack.js",
      "wwwroot/js/moment.min.js",
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]    

This should be enough to get you started, but if you need more information then please do check out Rion's excellent article linked above.

Autoprefixing CSS files

The bundler and minifier extension is great, but one thing I wanted to be able to do, and couldn't find any information on how to do it, was autoprefixing my CSS files so that I don't have to try and remember all of the vendor prefixes for CSS rules such as display: flex; and border-radius: 5px;

I don't know if this is a feature that just hasn't yet made it's way into the extension or not, but if it appears later please let me know in the comments as the following solution seems like a bit of a step backwards to me as we have to go back to relying on gulp!

Setting up gulp with PostCSS and Autoprefixer

The first thing to do is make sure your project has a package.json file in the root directory, and then create a devDependencies section as follows:

"devDependencies": {
    "autoprefixer": "^6.6.1",
    "gulp": "^3.9.1",
    "gulp-postcss": "^6.2.0"
}

Next, if you don't already have one, create a gulpfile.js at the root of your project. The contents of this file are going to be very simple, as we only need a single function:

var gulp = require('gulp'),
    postcss = require('gulp-postcss'),
    autoprefixer = require('autoprefixer');

gulp.task('css', function() {
    return gulp.src('./wwwroot/css/site.css')
        .pipe(postcss([ autoprefixer() ]))
        .pipe(gulp.dest('./wwwroot/css'));
});

First we require the packages we added to our package.json file above. Next we define a simple 'css' gulp task to look for our wwwroot/css/site.css file, pipe it through PostCSS with the Autoprefixer plugin, and then finally overwrite the original file so that it can be picked up by the bundler and minifier extension as discussed earlier.

This is a very simple implementation of PostCSS and the Autoprefixer plugin. If you want to see what else can be achieved with these libraries (such as specifying which browers to prefix for), head over to their github repo and check out the official documentation.

Testing that the gulp task is working

We can make sure our task is working by creating a simple wwwroot/css/site.css file:

.flex {
    display: flex;
}

.radius {
    border-radius: 5px;
}

Make sure you've installed gulp globally on your dev machine by running npm install -g gulp, and then from a cmd prompt at the root of your project simply run gulp css

You should see some output in your terminal to confirm that the task has been run successfully, and if you then inspect the site.css file in your editor / IDE, you should see that it has been overwritten with something that looks a little like:

.flex {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
}

.radius {
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
}

It's all very well and good being able to prefix our CSS files with one simple terminal command, but what if we make some styling changes to our app and forget to run the gulp task before deploying to production?

Automating the process with project.json scripts

You probably use a modern browser such as chrome for local development and testing, and as such these vendor prefixes aren't necessary when building and testing your app on your local machine.

If this is the case, then we can add a simple prepublish script to our project.json file that will run when we are ready to deploy our app to production, and run the dotnet publish command from the terminal (or publish from within visual studio itself):

"scripts": {
    "prepublish": ["npm install", "gulp css", "dotnet bundle"]
}

If you now go back to your terminal, run dotnet publish and inspect the output, you should see something that resembles the following:

Publishing autoprefix for .NETCoreApp,Version=v1.0
[10:27:48] Using gulpfile S:\Work\Projects\autoprefix\gulpfile.js
[10:27:48] Starting 'css'...
[10:27:48] Finished 'css' after 135 ms
Bundling with configuration from S:\Work\Projects\autoprefix\bundleconfig.json
Processing wwwroot/css/site.min.css
  Bundled
  Minified
Processing wwwroot/js/site.min.js
  Bundled
  Minified

This confirms that our prepublish script was hooked into successfully, and now every time we deploy our app, any CSS rules that require vendor prefixes will be handled as long as we use dotnet publish to prepare our code for production.

If you would rather this script was run on every build, simply switch the script key in project.json from prepublish to precompile as follows:

"scripts": {
    "precompile": ["npm install", "gulp css", "dotnet bundle"]
}

Conclusion

We started out by looking at a couple of different methods of bundling and minifying CSS / JS files in ASP.NET Core 1.0.

After identifying the limitations of the Bundler and Minifier extension, we looked at how to setup gulp, PostCSS and the Autoprefixer plugin to process our CSS files and add any required browser prefixes to our styles.

Finally, we looked at how to automate this process to be run during our deployment process, or on every build of our application.

As I mentioned earlier, this method is not at all ideal, as we have to take a step backward from the bundler and minifier extension that alleviated the complexity of javascript gulp files. However, at least our gulp files are kept small and easier to digest than those that we had to write when using gulp for the bundling and minification pipeline as well.

If you have any suggestions on improving this process, or a different (and potentially better) way of solving this problem, then please let me know in the discussion section below.

Discussion