Skip to main content Skip to docs navigation

Bundle Chassis CSS and JavaScript with Webpack — loaders, the import order, and CSS extraction for production.

Webpack is a JavaScript module bundler that compiles modules and their dependencies into static assets. This guide walks through a fresh project; if you already have Webpack set up, jump to Import Chassis.

If you've read Installation, the moving parts are familiar — the only Webpack-specific work is configuring loaders so it can process Sass.

Setup

This guide assumes Node.js is installed and you're comfortable in a terminal.

  1. Create a project folder and initialize npm.

    mkdir my-project && cd my-project
    pnpm init
    
  2. Install Webpack and its plugins. webpack is the bundler core, webpack-cli lets you invoke it from the terminal, webpack-dev-server runs a local development server, and html-webpack-plugin lets us keep index.html in src/ instead of dist/. The --save-dev flag (or -D in pnpm) marks these as build-time dependencies.

    pnpm add -D webpack webpack-cli webpack-dev-server html-webpack-plugin
    
  3. Install Chassis CSS, its tokens, and Popper. Popper is needed by dropdowns, popovers, and tooltips; if you're not using those plugins you can omit it.

    pnpm add @chassis-ui/css @chassis-ui/tokens @popperjs/core
    
  4. Install Sass tooling. Webpack needs loaders to handle Sass and to add vendor prefixes via PostCSS.

    pnpm add -D autoprefixer css-loader postcss-loader sass sass-loader style-loader
    

Project structure

Create the source folders and the empty files you'll fill in below:

mkdir -p src/js src/scss
touch src/index.html src/js/main.js src/scss/styles.scss webpack.config.js

The result:

my-project/
├── src/
│   ├── js/
│   │   └── main.js
│   ├── scss/
│   │   └── styles.scss
│   └── index.html
├── package.json
├── pnpm-lock.yaml
└── webpack.config.js

Configure Webpack

Open webpack.config.js and add the bare-bones config below. It tells Webpack where to find the entry point, where to emit the bundle, and how the dev server should serve it.

'use strict'

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: './src/js/main.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    static: path.resolve(__dirname, 'dist'),
    port: 8080,
    hot: true
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
}

Fill in src/index.html so Webpack has something to render:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Chassis CSS w/ Webpack</title>
  </head>
  <body>
    <div class="container py-large px-medium mx-auto">
      <h1>Hello, Chassis CSS and Webpack!</h1>
      <button class="button primary">Primary button</button>
    </div>
  </body>
</html>

Add start and build scripts to package.json:

{
  "scripts": {
    "start": "webpack serve",
    "build": "webpack build --mode=production"
  }
}

You can now run pnpm start to launch the dev server, but at this point the page won't be styled because we haven't told Webpack how to handle Sass yet.

Import Chassis

Webpack needs the Sass loaders we installed earlier and a Sass entry point that imports the tokens before Chassis itself.

  1. Add the loader rules to webpack.config.js. Replace the existing module.exports with the version below — the new piece is the module.rules block.

    'use strict'
    
    const path = require('path')
    const autoprefixer = require('autoprefixer')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      mode: 'development',
      entry: './src/js/main.js',
      output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
      },
      devServer: {
        static: path.resolve(__dirname, 'dist'),
        port: 8080,
        hot: true
      },
      plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
      ],
      module: {
        rules: [
          {
            test: /\.scss$/,
            use: [
              'style-loader',     // Injects CSS into a <style> tag
              'css-loader',       // Resolves @import and url()
              {
                loader: 'postcss-loader',
                options: { postcssOptions: { plugins: [autoprefixer] } }
              },
              'sass-loader'       // Compiles Sass to CSS
            ]
          }
        ]
      }
    }
    
  2. Import tokens, then Chassis, in src/scss/styles.scss. The order matters — Chassis's _vendor.scss reads $cx-* variables that the tokens package defines, so the tokens import must come first.

    // 1. Token source (must come before chassis.scss)
    @import "@chassis-ui/tokens/dist/web/chassis/docs/main";
    
    // 2. Optional: settings overrides
    // $enable-rfs: false;
    // $enable-component-shadows: true;
    
    // 3. Chassis CSS
    @import "@chassis-ui/css/scss/chassis";
    

    The token build path follows the pattern dist/web/{brand}/{app}/main. The default Chassis package ships with the chassis/docs build; for your own brand or app, switch the path accordingly.

  3. Import the JavaScript and load the stylesheet from src/js/main.js.

    import '../scss/styles.scss'
    import * as chassis from '@chassis-ui/css'
    

    For smaller bundles, import only the plugins you use:

    import { Modal, Tooltip } from '@chassis-ui/css'
    
  4. Run the dev server.

    pnpm start
    

    The page should now render with Chassis styling — the heading uses the Chassis type scale, the button is filled with the primary palette, and the container is constrained.

Production optimizations

The style-loader setup above injects CSS via a <style> tag in the head. That's fine in development but suboptimal for production: it ships unstyled HTML to the browser before the JS evaluates. For production builds, extract the CSS to a separate file.

Extract CSS

Install the plugin:

pnpm add -D mini-css-extract-plugin

Wire it into the config in place of style-loader:

 const path = require('path')
 const autoprefixer = require('autoprefixer')
 const HtmlWebpackPlugin = require('html-webpack-plugin')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')

 module.exports = {
    plugins: [
-    new HtmlWebpackPlugin({ template: './src/index.html' })
+    new HtmlWebpackPlugin({ template: './src/index.html' }),
+    new MiniCssExtractPlugin()
   ],
   module: {
     rules: [
       {
         test: /\.scss$/,
         use: [
-          'style-loader',
+          MiniCssExtractPlugin.loader,
           'css-loader',
          ]
       }
     ]
   }
 }

After pnpm build, the bundle contains a separate dist/main.css that you can link from your HTML.

Extract embedded SVGs

Chassis CSS includes a handful of inline data: URI SVGs (form check marks, dropdown carets, validation icons). If your Content Security Policy blocks data: URIs in img-src or background-image, extract them to files using Webpack's asset modules:

 module: {
   rules: [
+    {
+      mimetype: 'image/svg+xml',
+      scheme: 'data',
+      type: 'asset/resource',
+      generator: { filename: 'icons/[hash].svg' }
+    },
     {
       test: /\.scss$/,
      }
   ]
 }

After pnpm build, the SVGs are emitted to dist/icons/ and the compiled CSS references them as files.

Next steps