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.