There are some nice articles explaining what are CSS Modules, so I recommend you reading them first:
CSS Modules are useful in the following scenarios:
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:
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