**What are CSS Modules? **#

There are some nice articles explaining what are CSS Modules, so I recommend you reading them first:

Why would you want to use CSS Modules with Angular?#

CSS Modules are useful in the following scenarios:

  1. Your web application is used as web-component or inside an iframe on other website and you want to prevent the host website to override your styles.
  2. You use ViewEncapsulation.None for your styles so it will help you to prevent conflicts in the styles of your components.
  3. You use external libraries that override your application styles and you want to prevent that.

How to use CSS Modules with Angular?#

We can use CSS Modules with Angular through postcss-modules and posthtml-css-modules.

First, postcss-modules hash all the class names in the styles files in build time.

Example: takes app.component.scss

.grid-container {
    display: grid; grid-template-rows: auto;
    grid-template-rows: 90px calc(100vh - 170px) 80px;
    width: 100vw; height: 100vh;
}

.header {
    background-color: #1ba0f7;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    img {
        width: 64px;
        height: 64px; 
    }
}

.footer {
    background-color: #1ba0f7;
}
<>

app.component.scss

And it hashes all the classes like this:

._3Kuna {
    display: grid;
    grid-template-rows: auto;
    grid-template-rows: 90px calc(100vh - 170px) 80px;
    width: 100vw;
    height: 100vh;
}

._2-SC8 {
    background-color: #1ba0f7;
    display: flex;
    align-items: center;
    justify-content: flex-start;

    img {
        width: 64px;
        height: 64px;
    }
}

._2w5qX {
    background-color: #1ba0f7;
}
<>

app.component.scss after using postcss-modules

Immediately, postcss-modules creates a .json file for each component style file where it stores the mapping between class names and hashed class names.

For example, the .json of app.component.scss is app.component.scss.json:

{
  "grid-container":"_3Kuna",
  "header":"_2-SC8",
  "footer":"_2w5qX"
}
<>

app.component.scss.json

Then, posthtml-css-modules takes every component.html file and changes the class names by their corresponding hashed class name.

In order to do that, we have to change the attributes class by css-modules, so posthtml-css-modules identify what name has to change.

For example, posthtml-css-modules will take app.component.html (you can see it has css-modules as attribute instead of class):

<div css-module="grid-container">
  <div css-module="header">
    <a href="https://angular.io/" target="_blank">
      <img src="assets/img/angular_logo.png">
    </a>
  </div>
  <div>
      <router-outlet></router-outlet>
  </div>
  <div css-module="footer">
  </div> 
</div>
<>

app.component.html

And it will replace css-module for class but leave the hashed value as value of the class:

<div class="_3Kuna">
  <div class="_2-SC8">
    <a href="https://angular.io/" target="_blank">
      <img src="assets/img/angular_logo.png">
    </a>
  </div>
  <div>
      <router-outlet></router-outlet>
  </div>
  <div class="_2w5qX">
  </div>  
</div>
<>

app.component.html after using posthtml-css-modules

That’s it how we use CSS Modules in a Angular Project, now the question is:

How we use postcss-modules and posthtml-css-modules every time we run our project?#

Thanks to @angular-builders/custom-webpack we can customize the build configuration of our Angular application.

First, we need to install the following packages:

npm install @angular-builders/custom-webpack 
postcss-modules posthtml-css-modules posthtml-loader raw-loader lodash -D
<>

Then, we need to customize the Angular build through @angular-builders/custom-webpack, therefore, I created the file: extra-webpack.config.js in the root of my project:

const postcssModules = require('postcss-modules');
const path = require('path');
const AngularCompilerPlugin = require('@ngtools/webpack');

module.exports = (config, options) => {
    /*  SCSS EXTEND */
    const scssRule = config.module.rules.find(x => x.test.toString().includes('scss'));
    const postcssLoader = scssRule.use.find(x => x.loader === 'postcss-loader');
    const pluginFunc = postcssLoader.options.plugins;
    const newPluginFunc = function () {
        var plugs = pluginFunc.apply(this, arguments);
        plugs.splice(plugs.length - 1, 0, postcssModules({ generateScopedName: "[hash:base64:5]" }));
        return plugs;
    }
    postcssLoader.options.plugins = newPluginFunc;

    /*  HTML EXTEND */
    config.module.rules.unshift(
        {
            test: /\.html$/,
            use: [
                { loader: 'raw-loader' },
                {
                    loader: 'posthtml-loader',
                    options: {
                        config: {
                            path: './',
                            ctx: {
                                include: { ...options },
                                content: { ...options }
                            }
                        },
                    }
                },
            ]
        },
    );

    const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin);
    const oldOptions = config.plugins[index]._options;
    oldOptions.directTemplateLoading = false;
    config.plugins.splice(index);
    config.plugins.push(new AngularCompilerPlugin.AngularCompilerPlugin(oldOptions));
    return config;
};

#angular #css-modules

Angular + CSS Modules
2.95 GEEK