Home > Software engineering >  CSS Modules import behaves differently in Gatsby vs Storybook
CSS Modules import behaves differently in Gatsby vs Storybook

Time:04-14

I am building out a Gatsby web app where each component has a .module.css file, a .jsx file, and a stories.jsx file. I'm using the following import line as required by Gatsby (albeit without the tree shaking):

import * as styles from ./"filename.module.css"

Then if, for example, I have a CSS selector in my filename.module.css called .button, the associate class name is accessible in the styles object as styles.button.

However, the same styles.button does not access the correct class name in Storybook, it returns undefined. After examining the styles object I see that the class names are contained in a default object within styles. I'm assuming I have to configure Storybook in a way that handles the import in the same way as Gatsby. I similarly found that using this following import works in Storybook but not Gatsby:

import styles from ./"filename.module.css"

Any recommendations on how to configure Storybook to handle the CSS modules import in the same way as Gatsby? Ideally I would like to avoid tree shaking the CSS module, and from some quick testing this does not seem to be the cause of the issue.

CodePudding user response:

It's webpack who tree shake your styles or not and what requires using one statement (import * as styles from "./filename.module.css") or another (import styles from "./filename.module.css"). It seems that you are using different versions on each project so the behavior of the CSS module also changes.

Among all options, I'd recommend using named imports as Gatsby requires because as I said, it allows webpack to tree shake your styles, improving the performance of the site.

Instead of:

import * as styles from "./filename.module.css"

It's better to import each specific CSS module and apply them individually:

import { style1, style2 } from "./filename.module.css"

Extracted from: Gatsby with PostCSS 8 - Attempted import error: 'component.module.css' does not contain a default export (imported as 'styles')

Using this second approach will be a valid syntax for your project.

If you still want to change Storybook's behavior (to avoid tree shaking), you can try configuring Storybook like the following (in your .storybook/main.js):

const path = require("path");

module.exports = {
  // You will want to change this to wherever your Stories will live
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
  core: {
    builder: "webpack5",
  },
  webpackFinal: async (config) => {
    // Prevent webpack from using Storybook CSS rules to process CSS modules
    config.module.rules.find(
      (rule) => rule.test.toString() === "/\\.css$/"
    ).exclude = /\.module\.css$/;

    // Tell webpack what to do with CSS modules
    config.module.rules.push({
      test: /\.module\.css$/,
      include: path.resolve(__dirname, "../src"),
      use: [
        {
          loader: "style-loader",
          options: {
            modules: {
              namedExport: true,
            },
          },
        },
        {
          loader: "css-loader",
          options: {
            importLoaders: 1,
            modules: {
              namedExport: true,
            },
          },
        },
      ],
    });
    // Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
    config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/];
    // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
    config.module.rules[0].use[0].options.plugins.push(
      require.resolve("babel-plugin-remove-graphql-queries")
    );
    return config;
  },
};

Other resources:

  • Related