CSS Modules in components library #
Usually, I prefer to use native css on my web applications, however global css is hard to scope, make it harder reuse and maintain, in the long term, when code start growing.
To solve that, I like to use css modules and keep css near to the component will be using it. Here I describe what I did to use css modules on a react components library started with TDSX:
I needed to install postcss and rollup-plugin-postcss:
yarn lerna add postcss --scope=@mr/ui-web --devyarn lerna add rollup-plugin-postcss --scope=@mr/ui-web --devAfter that I needed to update packages/ui-web/tsdx.config.js to override some rollup configurations:
const postcss = require('rollup-plugin-postcss');
module.exports = {
rollup(config, options) {
config.plugins.push(
postcss({
modules: true,
extract: true
})
);
return config;
},
};
Now, I check if build keeps working:
yarn lerna run build --scope=@mr/ui-webNothing happens because css was not added yet. So, I added the following css module, using the *.module.css convention (this will be important for storybook, later).
packages/ui-web/components/BookCard/styles.module.css:
.card {
border: 2px solid black;
}
Updated packages/ui-web/components/BookCard/index.tsx to use css created:
import React from 'react';
// @ts-ignore
import styles from './styles.module.css';
export default function BookCard({ book }: { book: any }) {
return (
<div className={styles.card}>
<h2>Test: {book.title}</h2>
<p>{book.author}</p>
<small>{book.tag}</small>
</div>
);
}
Note: We need to configure typescript to handle styles.module.css. For now, I am using // @ts-ignore. In the Typescript section we will have a workaround for that.
At packages/ui-web/tsdx.config.js, I used extract: true, so a css file is generated on the final build at distfolder. When we execute:
yarn lerna run build --scope=@mr/ui-weba file packages/ui-web/dist/ui-web.cjs.production.min.css is generated with something like:
.styles-modules_card__2yoe0 {
border: 2px solid black;
}
styles-module_card__2yoe0 is autogenerated, and will be the css class added to our component in the final build, as we can see at packages/ui-web/dist/ui-web.cjs.production.min.js:
return t.createElement("div",{className:"styles-module_card__2yoe0"}Applications using this library should import this css file. If we do not want that, we could remove extract:true and the css will be injected, through javascript. I think in big projects it could lead to performance problems, browsers are more performant handling css, than javascript.
Storybook #
To be able to use storybook we need to add storybook-css-modules-preset:
yarn lerna add storybook-css-modules-preset --scope=@mr/ui-web --devAnd update pacakges/ui-web/.storybook/main.js:
module.exports = {
stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'storybook-css-modules-preset',
],
// https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration
typescript: {
check: true, // type-check stories during Storybook build
},
};
Now, we can visualize the BookCard component when we execute:
yarn lerna run storybook --scope=@mr/ui-webTypescript #
To solve the typescript problem, and remove the @ts-ignore I do the following. I got this idea from here:
Created packages/ui-web/src/typings.d.ts:
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
However, I would like to have this plugin working. I will keep an eyes on it.
Published