Getting Started With Webpack - Part Four: SASS and CSS Loaders

One of Webpack's features that sets it apart from similar tools is the ability to load modules other than JavaScript such as CSS and HTML. In this post we'll look at how Webpack handles pre-processing SASS and allows us to write modular CSS.

To get started install the following packages:

$ npm install css-loader sass-loader extract-text-webpack-plugin --save-dev

Next, configure the loaders and plugin:

// webpack.config.js

var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: './src/index.js',
    output: {
        path: './public/',        
        filename: 'bundle.js'
    },
    devServer: {
        contentBase: './public/'
    },
    module: {
        loaders: [
            {test: /\.js$/, exclude: /node_modules/, loader: 'babel'},
            {test: /\.scss$/, loader: ExtractTextPlugin.extract('css!sass')}
        ]
    },
    plugins: [
        new ExtractTextPlugin("style.css")
    ]
}

We've told Webpack to first run any file that ends in ".scss" through the sass-loader to transpile it to css, then run the css through the css-loader which loads the style declarations into a JavaScript module, and finally the ExtractTextPlugin extracts the css into one main bundle file, "style.css".

To test this out, let's display our users on the screen and include some styles in our bundle. First we need to add a stylesheet link and container div to display the users in our index.html file:

// public/index.html

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container"></div>
        <script src="bundle.js"></script>
    </body>
</html>

Then add some styles using Sass:

// src/_colors.scss

$primary-color: #34495e;
// src/user.scss

@import 'colors';

.user {
    color: $primary-color;
    font-family: sans-serif;
}

Finally, we can import this style declaration directly in our JavaScript and build the HTML to display our users.

// src/index.js

import sortBy from 'lodash/collection/sortBy';
import {users} from './users';
import {User} from './User';
import './user.scss';

var sortedUsers = sortBy(users, 'name')
    .map(function(user) {
        return new User(user.name, user.age);
    });

document.querySelector('.container').innerHTML = buildHTML();

function buildHTML() {
    var html = '';
    sortedUsers.forEach(user => {
        html += `<p class="user">${user.display}</p>`;
    });
    return html;
}

Load the page in your browser and you should see each user's display message in the color we specified. If you still have webpack-dev-server running you can update the $primary-color variable in src/_colors.scss and see the color update in the browser.

A great feature of Webpack's css-loader is locally scoped style declarations. This allows you to write modular CSS without worrying about global naming collisions. To test this feature out, make the following adjustments:

// src/user.scss

@import 'colors';

:local(.user) {
    color: $primary-color;
    font-family: sans-serif;
}

In src/index.js adjust the import statement:

import styles from './user.scss';

The module will return an object that we can use to access the class name that is generated by css-loader. Adjust the markup for each user to use the styles object:

`<p class="${styles.user}">${user.display}</p>`

Load the page in your browser and you'll see that css-loader generates a hash for the class name on each of the tags and also in the "styles.css" file. With this feature you shouldn't ever have to worry about global CSS naming collisions again!

In the next post, we'll look at creating multiple entry point files and extracting common code into a separate bundle.

Written on June 9, 2015