⚠️ This module is no longer maintained ⚠️

Extract CSS block plugin for webpack

Build status

I'll admit, it's not a catchy name. Compatible with Webpack 1.x and 2.x.

What and why?!

Sometimes you want to separate your monolithic stylesheets into multiple files; maybe to excise critical styles or split into lazy loadable bundles.

To do this you need multiple entry points and so any context created while building each stylesheet is lost. For a flat dependency tree this may not be a problem but for more complex projects where multiple components share dependencies it can be; think duplicated output and context switching to check where or if a dependency is already used... not fun!

This plugin enables you to maintain a single entry point but mark specific blocks of rules to be extracted into separate files.

Installation

First install the plugin and save it to your package manifest dev dependencies:

npm i --save-dev extract-css-block-webpack-plugin

Next apply the plugin via your webpack config. The plugin can only be used with separate stylesheets and therefore must be added after the extract text plugin:

const ExtractCssBlockPlugin = require('extract-css-block-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
  ...
  module: {
    loaders: [
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract(['css', 'sass'])
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('[name]'),
    new ExtractCssBlockPlugin()
  ]
}

The plugin accepts a map of options, currently there is only one:

Option Description Default
match Filters files to transform /\.css$/

Basic usage

To mark blocks of rules as targets for extraction use 'loud' (!) start and end comments. For example, given this Sass main.scss entry point:

@import "shared-ui/variables";
@import "shared-ui/functions";
@import "shared-ui/mixins";

/*! start:critical.css */
@import "shared-ui/component/normalize";
@import "shared-ui/component/grid";
@import "shared-ui/component/typography";
/*! end:critical.css */

@import "component/tabs";
@import "component/modal";
@import "layout/list-page";
@import "layout/content-page";

/*! start:comments.css */
@import "component/comments";
@import "component/comment-form";
/*! end:comments.css */

Webpack will write 3 stylesheets:- main.css, critical.css and comments.css.

Advanced usage

Blocks may be nested:

/*! start:header.css */
.header {
  padding: 10px 20px;
  background: #333;
  color: #FFF;
}
/*! start:header-menu.css */
.header-menu {
  list-style: none;
  margin: 0 15px;
}
/*! end:header-menu.css */

.header-logo {
  float: left;
  border: 0;
}
/*! end:header.css */

Blocks may also be used many times and their contents will be aggregated:

/*! start:header.css */
.header {
  padding: 10px 20px;
  background: #333;
  color: #FFF;
}
/*! end:header.css */

.header-menu {
  list-style: none;
  margin: 0 15px;
}

/*! start:header.css */
.header-logo {
  float: left;
  border: 0;
}
/*! end:header.css */

Source maps

To enable source maps ensure that webpack, the CSS plugin and any preceeding pre-processors have source maps enabled:

module.exports = {
  ...
  module: {
    loaders: [
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract(['css?sourceMap', 'sass?sourceMap'])
      }
    ]
  },
  devtool: 'source-map'
}

Why not use PostCSS?

There is a PostCSS version of this plugin available but as PostCSS is not designed to write multiple files it is a bit hacky.