Webpack 3 Sass cssnano Autoprefixer Workflow

Basic Webpack 3 Sass preprocessor workflow using cssnano for postprocessing minification and optimization. The Autoprefixer PostCSS plugin is also included in this configuration to automatically add vendor prefixes for only the browsers that need to be supported.

Before getting started, install Node.js and NPM. The default package manager for the Node.js JavaScript runtime environment, NPM is included with the Node.js installation.

Project

Navigate to the project root in your CLI, such as Terminal in OS X, PowersShell in Windows.

Enter npm init to interactively create a package manifest file (package.json). As devDependencies are installed using the --save-dev option, the package.json file will be updated so the node modules can be re-installed using npm install or with its shorthand alias npm i.

npm init

Webpack 3

Webpack is a module bundler that generates static assets representing modules and their dependencies from a generated dependency graph. This enables a modular approach to web development that can be extended by using loaders with tasks that are performed when bundling files together.

Webpack version 3 was released earlier this year and as of this writing, version 3.8.1 is the latest. Install the latest version using npm install.

npm install --save-dev webpack

Open the project folder in a code editor, such as VS Code. Inspect the package.json file, webpack with its version number should now be listed under the devDependencies node. For example:

package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^3.8.1"
  }
}

Webpack Configuration

Create a new JavaScript file in the project root named webpack.config.js. At the top of this file define these required modules as constants: path, webpack and extract-text-webpack-plugin.

webpack.config.js
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

We’ve already installed webpack, and path is a core node module. Let’s install the Extract Text Plugin that will be used to extract the css from the bundle into a separate file.

npm install --save-dev extract-text-webpack-plugin

Next, create the webpack configuration object to export to the CLI.

webpack.config.js
...

module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: {
    /* app: './js/index.js',*/
    css: './sass/main.scss',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'bundle.js'
  },
  module: {
    rules: [
    {
      test: /\.(css|scss)$/,
      use: ExtractTextPlugin.extract({
        // TODO - Add Loaders
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin('style.css'),
  ]
}

Throughout this tutorial you will encounter an ellipsis … in the code examples. These are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. To view the entire file, examine the source code.

Install Loaders

Before we define the loaders in the webpack configuration, let’s install them.

SASS Loader compiles Sass to CSS and since it requires node-sass, install both sass-loader and node-sass.

npm install --save-dev sass-loader node-sass

PostCSS Loader processes CSS with PostCSS.

npm install --save-dev postcss-loader

CSS Loader resolves import at-rules and url functions in the CSS.

npm install --save-dev css-loader

Style Loader inlines <style></style> in the DOM.

npm install --save-dev style-loader

To handle processing of relative URLs in the Sass for things like fonts and images, see part II.

Define Loaders

In our configuration, we are extracting the CSS to its own file with the ExtractTextPlugin. Therefore we will define our loaders for processing Sass and CSS in the plugins use option. The fallback option is used when the CSS is not extracted.

webpack.config.js
...

module: {
  rules: [
  {
    test: /\.(css|scss)$/,
    use: ExtractTextPlugin.extract({
      fallback: 'style-loader',
      use: [
        {
          loader: 'css-loader',
          options: {
            minimize: true || {/* CSSNano Options */}
          }
        },
        {
          loader: 'postcss-loader'
        },
        {
          loader: 'sass-loader'
        }
      ]
    })
  }]
},

Note that the loaders are ordered from bottom to top or right to left. Loaders act like functions, that’s why it’s from right to left. For example, css-loader(postcss-loader(sass-loader(resource)))

The next section covers installing and configuring the Autoprefixer PostCSS plugin, running the Webpack build and creating demo Sass and html to test the output.


Autoprefixer

Install this PostCSS plugin that adds vendor prefixes to CSS rules by referencing Can I Use data.

npm install --save-dev autoprefixer

Create a PostCSS configuration module in the project root for requiring the autoprefixer plugin.

postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

Autoprefixer uses the browserlist library to target browsers. Configure autoprefixer browser support by adding a list of supported browsers to the package.json file. In this example, we target browsers greater than 1% according to global usage statistics and Internet Explorer versions greater than 9.

package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "browserslist": [
    "> 1%",
    "ie > 9"
  ],

  ...
}

npm-run-script

To run a webpack build, add "build": "webpack" to the package’s “scripts” object.

package.json
...

  "browserslist": [
    "> 1%",
    "ie > 9"
  ],
  "scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}

Update the run scripts with a dev script command to watch files and build whenever updates are saved. Additionally, add some flags to the dev and build scripts for the console output.

package.json
...

  "scripts": {
    "dev": "webpack --watch --progress --colors",
    "build": "webpack --progress --hide-modules",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}

Demo

Some Sass known to require vendor pre-fixes is needed to test and demo the build. For example, create a src/sass folder and add these scss files.

main.scss
@import 'modules/js';
@import 'modules/loading';

Create a modules folder in the sass folder for these two files.

_js.scss
// classes used in javascript events

.jshide {
    display: none;
}
_loading.scss
/* Absolute Center Spinner */
.loading {
    position: fixed;
    z-index: 999;
    height: 2em;
    width: 2em;
    overflow: show;
    margin: auto;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;

    /* Transparent Overlay */
    &:before {
        content: '';
        display: block;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0,0,0,0.3);
    }
}

/* :not(:required) hides these rules from IE9 and below */
.loading:not(:required) {
      /* hide "loading..." text */
      font: 0/0 a;
      color: transparent;
      text-shadow: none;
      background-color: transparent;
      border: 0;
}

.loading:not(:required):after {
      content: '';
      display: block;
      font-size: 10px;
      width: 1em;
      height: 1em;
      margin-top: -0.5em;
      animation: spinner 1500ms infinite linear;
      border-radius: 0.5em;
      box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
}

/* Animation */
@-webkit-keyframes spinner {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
@-moz-keyframes spinner {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
@-o-keyframes spinner {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
@keyframes spinner {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

Create an index.html file in the project root to output the running demo in a web browser.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Loading Spinner Demo</title>
    <link rel="stylesheet" href="dist/style.css">
</head>
<body>
    <div class="loading jshide">Loading…</div>
    <button>Load</button>
    <script>

        var div = document.querySelector('div'),
            btn = document.querySelector('button');

        btn.addEventListener('click', function() {

            div.className = 'loading';

            btn.disabled = true;

            setTimeout(function() {

                btn.disabled = false;

                div.className = 'loading jshide';

            }, 3000);

        }, false);

    </script>
</body>
</html>

Build

Run the dev or build command from the package.json scripts object. Webpack outputs the processed style.css file into the dist folder.

Watch files and build incrementally. Intended development workflow.

npm run dev

Build once and exit.

npm run build
Source Code

Part 1 of 3 in the Webpack 3 Sass cssnano Autoprefixer Workflow series.

Webpack 3 Sass cssnano Autoprefixer Workflow II

comments powered by Disqus