Skip to main contentSkip 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.

Installation covers the moving parts — the only Webpack-specific work here is configuring loaders to 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.

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

    Shell
    pnpm add -D webpack webpack-cli webpack-dev-server html-webpack-plugin
  3. Install Chassis CSS and its tokens. Add the optional peer dependencies for the components that need them, or omit both and use chassis.bundle.* which includes them.

    Shell
    pnpm add @chassis-ui/css @chassis-ui/tokens
    pnpm add @floating-ui/dom vanilla-calendar-pro   # optional
  4. Install Sass tooling. Webpack needs loaders to handle Sass and to run PostCSS — for vendor prefixes, and for the --cx- custom-property prefix Chassis's own components and JS plugins read at runtime.

    Shell
    pnpm add -D autoprefixer css-loader postcss-loader postcss-prefix-custom-properties sass sass-loader style-loader

Project structure

Create the source folders and starter files:

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

The result:

TEXT
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.

JavaScript
'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:

HTML
<!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-lg px-md 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:

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

You can now run pnpm start to launch the dev server, but the page won't be styled yet — Webpack isn't configured to handle Sass.

Import Chassis

Webpack needs the Sass loaders from the previous step and a Sass entry point. The sass-loader requires loadPaths entries pointing to the project's Sass directory and to node_modules, so both token overrides and the framework's own default tokens can be resolved — the project directory must come first so an override wins.

  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. postcss-loader is left without inline options here — see the next step for the config file it auto-loads.

    JavaScript
    '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' })
      ],
      module: {
        rules: [
          {
            test: /\.scss$/,
            use: [
              'style-loader',     // Injects CSS into a <style> tag
              'css-loader',       // Resolves @import and url()
              'postcss-loader',   // Runs postcss.config.mjs (next step)
              {
                loader: 'sass-loader',
                options: {
                  sassOptions: {
                    loadPaths: [
                      path.resolve(__dirname, 'src/scss'),                                    // project token override
                      path.resolve(__dirname, 'node_modules/@chassis-ui/css/scss/vendor'),    // framework's default tokens
                      path.resolve(__dirname, 'node_modules')                                 // bare package specifiers (@chassis-ui/tokens/…)
                    ]
                  }
                }
              }
            ]
          }
        ]
      }
    }
  2. Add postcss.config.mjs. postcss-loader auto-loads it. The .mjs extension keeps it an ES module regardless of package.json's own "type" field, since @chassis-ui/css/postcss is ESM-only.

    JavaScript
    // postcss.config.mjs
    import autoprefixer from 'autoprefixer'
    import { chassisPrefix } from '@chassis-ui/css/postcss'
    
    export default {
      plugins: [chassisPrefix(), autoprefixer]
    }

    chassisPrefix() adds the --cx- prefix some Chassis components and JS plugins read at runtime (--cx-carousel-interval, --cx-breakpoint-*) — Sass only ever writes the unprefixed name.

  3. Fill in src/scss/styles.scss. The token source resolves through _vendor.scss before the rest of Chassis processes its variables, so a single @use directive is all that's needed.

    SCSS
    // 1. Chassis CSS — token source resolves via the loadPaths entry in webpack.config.js
    @use "@chassis-ui/css/scss/chassis";
    
    // 2. Optional: settings overrides use the `with (...)` syntax
    // @use "@chassis-ui/css/scss/chassis" with (
    //   $enable-fluid-font-sizes: false,
    //   $enable-component-shadows: true,
    // );

    The default token build (chassis/docs) is resolved via the loadPaths entries above. To switch to a custom token set, place a _chassis-tokens.scss on an earlier loadPaths entry — see Customize → Sass → Token sources for details.

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

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

    For smaller bundles, import only the required plugins:

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

    Shell
    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.

Build 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

The mini-css-extract-plugin replaces style-loader to write the compiled CSS to a separate file, enabling the browser to download the stylesheet in parallel with the JavaScript bundle. Install it:

Shell
pnpm add -D mini-css-extract-plugin

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

DIFF
 const path = require('path')
 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 ready to link from any HTML page.

Next steps

These docs cover JavaScript integration, Sass customization, and bundle optimization for a Webpack-based Chassis project.